双链表结构说明示意图(精)
数据结构与算法之PHP实现链表类(单链表双链表循环链表)
数据结构与算法之PHP实现链表类(单链表双链表循环链表)链表是由⼀组节点组成的集合。
每个节点都使⽤⼀个对象的引⽤指向它的后继。
指向另⼀个节点的引⽤叫做链。
链表分为单链表、双链表、循环链表。
⼀、单链表插⼊:链表中插⼊⼀个节点的效率很⾼。
向链表中插⼊⼀个节点,需要修改它前⾯的节点(前驱),使其指向新加⼊的节点,⽽新加⼊的节点则指向原来前驱指向的节点(见下图)。
由上图可知,B、C之间插⼊D,三者之间的关系为current为插⼊节点的前驱节点new->next = current->next // 新节点D指向B的后继节点Ccurrent->next = new // B节点指向新节点D删除:从链表中删除⼀个元素,将待删除元素的前驱节点指向待删除元素的后继节点,同时将待删除元素指向 null,元素就删除成功了(见下图)。
由上图可知,A、C之间删除B,三者之间的关系为current为要删除节点的前驱节点current->next = current->next->next // A节点指向C节点具体代码如下:<?php// 节点类class Node {public $data; // 节点数据public $next; // 下⼀节点public function __construct($data) {$this->data = $data;$this->next = NULL;}}// 单链表类class SingleLinkedList {private $header; // 头节点function __construct($data) {$this->header = new Node($data);}// 查找节点public function find($item) {$current = $this->header;while ($current->data != $item) {$current = $current->next;}return $current;}// (在节点后)插⼊新节点public function insert($item, $new) {$newNode = new Node($new);$current = $this->find($item);$newNode->next = $current->next;$current->next = $newNode;return true;}// 更新节点public function update($old, $new) {$current = $this->header;if ($current->next == null) {echo "链表为空!";}while ($current->next != null) {if ($current->data == $old) {break;}$current = $current->next;}return $current->data = $new;}// 查找待删除节点的前⼀个节点public function findPrevious($item) {$current = $this->header;while ($current->next != null && $current->next->data != $item) { $current = $current->next;}return $current;}// 从链表中删除⼀个节点public function delete($item) {$previous = $this->findPrevious($item);if ($previous->next != null) {$previous->next = $previous->next->next;}}// findPrevious和delete的整合public function remove($item) {$current = $this->header;while ($current->next != null && $current->next->data != $item) { $current = $current->next;}if ($current->next != null) {$current->next = $current->next->next;}}// 清空链表public function clear() {$this->header = null;}// 显⽰链表中的元素public function display() {$current = $this->header;if ($current->next == null) {echo "链表为空!";return;}while ($current->next != null) {echo $current->next->data . "  ";$current = $current->next;}}}$linkedList = new SingleLinkedList('header');$linkedList->insert('header', 'China');$linkedList->insert('China', 'USA');$linkedList->insert('USA','England');$linkedList->insert('England','Australia');echo '链表为:';$linkedList->display();echo "</br>";echo '-----删除节点USA-----';echo "</br>";$linkedList->delete('USA');echo '链表为:';$linkedList->display();echo '-----更新节点England为Japan-----';echo "</br>";$linkedList->update('England', 'Japan');echo '链表为:';$linkedList->display();//echo "</br>";//echo "-----清空链表-----";//echo "</br>";//$linkedList->clear();//$linkedList->display();// 输出:链表为:China USA England Australia-----删除节点USA-----链表为:China England Australia-----更新节点England为Japan-----链表为:China Japan Australia⼆、双链表单链表从链表的头节点遍历到尾节点很简单,但从后向前遍历就没那么简单了。
双向链表
第8讲 双向链表● 循环单链表的出现,虽然能够实现从任一结点出发沿着链能找到其前趋结点,但时间耗费是O (n) 。
● 如果希望从表中快速确定某一个结点的前趋,另一个解决方法就是在单链表的每个结点里再增加一个指向其前趋的指针域prior 。
这样形成的链表中就有两条方向不同的链,我们称之为双向链表。
● 双向链表的结构定义如下:typedef struct DNode{ ElemType data ;struct DNode *prior ,*next ;}DNode, * DoubleList ;● 双向链表的结点结构如图所示。
图:双链表的结点结构注:● 双向链表也是由头指针唯一确定的,● 增加头结点能使双链表的某些运算变得方便● 由于在双向链表中既有前向链又有后向链,寻找任一个结点的直接前驱结点与直接后继结点变得非常方便。
● 设指针p 指向双链表中某一结点,则有下式成立:p->prior->next = p = p->next->prior●在双向链表中,那些只涉及后继指针的算法,如求表长度、取元素、元素定位等,与单链表中相应的算法相同,● 但对于前插和删除操作则涉及到前驱和后继两个方向的指针变化,因此与单链表中的算法不同。
1、 双向链表的前插操作【算法思想】欲在双向链表第i 个结点之前插入一个的新的结点,则指针的变化情况如图所示:… p …s->prior=p->prior; ①p->prior->next=s;②s->next=p; ③p->prior=s;④【算法描述】int DlinkIns(DoubleList L,int i,ElemType e){DNode *s,*p;… /*先检查待插入的位置i是否合法(实现方法同单链表的前插操作)*/… /*若位置i合法,则找到第i个结点并让指针p指向它*/s=(DNode*)malloc(sizeof(DNode));if (s){ s->data=e;s->prior=p->prior; ①p->prior->next=s; ②s->next=p; ③p->prior=s; ④r eturn TRUE;}else return FALSE;}2、双向链表的删除操作【算法思想】欲删除双向链表中的第i个结点,则指针的变化情况如图所示:p->prior->next=p->next; ①p->next->prior=p->prior; ②free(p);【算法描述】int DlinkDel(DoubleList L,int i,ElemType *e){DNode *p;… /*先检查待插入的位置i 是否合法(实现方法同单链表的删除操作)*/… /*若位置i 合法,则找到第i 个结点并让指针p 指向它*/*e=p->data;p->prior->next=p->next; ①p->next->prior=p->prior; ②free(p);return TRUE;}3、 双向循环链表双向链表可以有循环表,称为双向循环链表。
双链表和循环链表
4/29
1、双链表中结点的插入和删除
双链表插入结点的演示
在*p结点之后插入结点*s p
…
a
b
…
❖
s
c
操作语句: s->next = p->next ❖ p->next->prior = s s->prior = p p->next = s
插入完毕
5/29
双链表删除结点的演示
//查找第i-1个结点
查找第i-1个结点*p
13/29
if (p==NULL)
//未找到第i-1个结点
return false;
else
//找到第i-1个结点*p
{ q=p->next; if (q==NULL)
//q指向第i个结点 //当不存在第i个结点时返回false
return false;
L->next->prior=s;
L->next=s;
s->prior=L;
}
}
L∧插入 ∧源自sai9/29
尾插法建立双链表:由含有n个元素的数组a创建带头结 点的双链表L。
void CreateListR(DLinkNode *&L,ElemType a[],int n) { DLinkNode *s,*r;
L
a1
…
an ∧
3/29
对于双链表,采用类似于单链表的类型定义,其结点 类型DLinkNode定义如下:
typedef struct DNode { ElemType data;
struct DNode *prior; struct DNode *next; } DLinkNode;
第3周线性表(下)第2讲-循环链表
循环链表是另一种形式的链式存储结构形式。
循环单链表:将表中尾节点的指针域改为指向表头节点,整个链表形成一个环。
由此从表中任一节点出发均可找到链表中其他节点。
循环双链表:形成两个环。
节点类型与非循环单链表的相同节点类型与非循环双链表的相同线性表(a 1, a 2, …, a i , … a n )映射逻辑结构存储结构a 1a 2a n…L带头节点循环单链表示意图1、循环单链表与非循环单链表相比,循环单链表:链表中没有空指针域p所指节点为尾节点的条件:p->next==LL pa1a2a n…【例2-8】某线性表最常用的操作是在尾元素之后插入一个元素和删除第一个元素,故采用存储方式最节省运算时间。
A.单链表B.仅有头结点指针的循环单链表C.双链表D.仅有尾结点指针的循环单链表D.仅有尾结点指针的循环单链表a 1a2a n…L在尾元素之后插入一个元素删除第一个元素时间复杂度均为O(1)选择D线性表(a 1, a 2, … , a i , … a n )映射逻辑结构存储结构a 1a 2a n…L带头节点循环双链表示意图2、循环双链表与非循环双链表相比,循环双链表:链表中没有空指针域p所指节点为尾节点的条件:p->next==L一步操作即L->prior可以找到尾节点p La1a2a n…【例2-9】如果对含有n(n>1)个元素的线性表的运算只有4种,即删除第一个元素、删除尾元素、在第一个元素前面插入新元素、在尾元素的后面插入新元素,则最好使用。
A.只有尾结点指针没有头结点的循环单链表B.只有尾结点指针没有头结点的非循环双链表C.只有首结点指针没有尾结点指针的循环双链表D.既有头指针也有尾指针的循环单链表a 1a 2a n…LC.只有首结点指针没有尾结点指针的循环双链表删除第一个元素删除尾元素在第一个元素前面插入新元素在尾元素的后面插入新元素时间复杂度均为O(1)选择C【例2-10】设计判断带头节点的循环双链表L(含两个以上的节点)是否对称相等的算法。
Linux内核中的双循环链表解析
双循环链表传统实现在传统的双循环链表实现中,如果创建某种数据结构的双循环链表,通常采用的办法是在这个数据结构的类型定义中加入两个(指向该类型对象的)指针next和prev。
例如:typedef struct foo {…struct foo *prev;struct foo *next;…} foo_t;这里给出了对应的节点结构、空的双循环链表和非空的双循环链表示意图。
Linux内核中双循环链表实现在linux内核中,有大量的数据结构需要用到双循环链表,例如进程、文件、模块、页面等。
若采用双循环链表的传统实现方式,需要为这些数据结构维护各自的链表,并且为每个链表都要设计插入、删除等操作函数。
(由于用来维持链表的next和prev指针指向对应类型的对象,因此一种数据结构的链表操作函数不能用于操作其它数据结构的链表。
)在Linux源代码树的include/linux/list.h文件中,采用了一种(数据结构)类型无关的双循环链表实现方式。
其思想是将指针prev和next从具体的数据结构中提取处理构成一种通用的"双链表"数据结构list_head,而list_head被作为一个成员嵌入到要拉链的数据结构(被称为宿主数据结构)中。
这样,只需要一套通用的链表操作函数就可以将list_head成员作为"连接件",把宿主数据结构链接起来。
如下图所示:在Linux内核中的双循环链表实现方式下:1. 链表结构作为一个成员嵌入到宿主数据结构内;2. 可以将链表结构放在宿主结构内的任何地方;3. 可以为链表结构取任何名字;4. 宿主结构可以有多个链表结构。
下面我们将基于Linux 2.4.21分析Linux内核双循环链表的实现及应用。
声明和初始化链表结构定义如下:struct list_head {struct list_head *next, *prev;};我们可以用struct list_head声明一个链表节点。
双向链表及模板
C++中链表例子(有详细的注释)学c++的时候大家一定会对链表很难弄明白所以就给大家一个例子重点的说明了链表的用法还加了很详细的注释例子:#include <iostream>using namespace std;struct Node //很多同学对这个名字很模糊,节点的英文就是Node{ //不一定非要用Node的。
int data; //节点中的数据结构体Node的成员变量Node* next; //指向下一节点的指针,习惯用next命名结构体Node的成员变量Node( const int& d=int() ) //结构体也可以有构造函数,d=T()来指定默认值:data(d),next(NULL) //用构造函数来初始化成员变量data和指针{} //所有数据类型,默认初始化都为0,这里data默认初始化为0};//老师是把Node作为封装类的内包结构体,这里我给分开写一下class Chain //封装链表{private: //数据成员通常都是private的Node* head; //首先,我们要一个Node型的指针来保存链表的第一个节点;int length; //再用一个整型变量来记录当前链表内的节点数public:Chain() //在构造函数里建立一个空链表,即head指向NULL:head(NULL),length(0){} //节点数为0;//这里小小插句嘴:当我们在类中定义函数时(不是声明),相当于在前面加上一个inline 修饰void delall() //见名知意,这个函数用来删除链表中的所有节点{Node* pdel; //定义一个Node型指针用来保存要删除的节点while( head != NULL ) //当head的指向不为NULL时,就是链表中还存在节点{pdel = head; //这里备份head的当前指向节点head = head->next; //把head的指向改变为下一节点delete pdel; //把head的原指向给删除掉} //如此一直下去,尾节点的next肯定是指向NULL的,那删除最后一个的时候//head就被赋值为NULL,不满足循环条件,退出循环length = 0; //把节点数归零}~Chain(){ delall(); } //在析构函数中调用delall函数,来完成对象销毁时清理工作//这样一个链表必须具备的功能就实现了。
数据结构中的双向链表实现和应用场景
数据结构中的双向链表实现和应用场景双向链表是一种常用的数据结构,它在许多实际应用中都发挥着重要的作用。
本文将介绍双向链表的实现原理以及一些常见的应用场景。
一、双向链表的实现原理双向链表由一系列节点组成,每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。
相比于单向链表,双向链表可以实现双向遍历,提高了一些操作的效率。
1.1 节点定义双向链表的节点通常由数据域和两个指针域组成,例如:```struct Node {int data; // 节点数据Node* prev; // 前一个节点指针Node* next; // 后一个节点指针};```1.2 插入操作在双向链表中插入一个节点可以分为两种情况:在表头插入和在表尾插入。
在表头插入时,只需修改原来头节点的prev指针为新节点的地址,并将新节点的next指针指向原头节点即可。
在表尾插入时,需要先找到原来的尾节点,然后将尾节点的next指针指向新节点的地址,并将新节点的prev指针指向尾节点的地址。
1.3 删除操作删除操作与插入操作类似,同样分为在表头和表尾删除节点。
在表头删除时,只需将头节点的next指针指向新的头节点,同时将新头节点的prev指针置为空。
在表尾删除时,需要先找到尾节点的前一个节点,然后将该节点的next指针置为空。
1.4 查找操作双向链表支持从前向后和从后向前两种遍历方式。
从前向后遍历时,我们可以利用节点的next指针不断向后遍历得到所有节点。
同样,从后向前遍历时,可以利用节点的prev指针不断向前遍历得到所有节点。
二、双向链表的应用场景双向链表广泛应用于各种软件和系统中,下面列举了一些常见的应用场景。
2.1 浏览器的历史记录在浏览器中,经常需要记录用户浏览过的网页历史记录。
这时可以使用双向链表来实现。
每当用户访问一个新的网页,就在双向链表中插入一个新节点,同时将新节点的next指针指向前一个节点,prev指针指向后一个节点。
PHP之双向链表(SplDoublyLinkedList)简介
PHP SPL标准库里实现了几种简单的线性表和树型结构,其中包括了双链表和双链表实现的队列和栈、最大堆、最小堆和优先队列。
双链表是一种重要的线性存储结构,对于双链表中的每个节点,不仅仅存储自己的信息,还要保存前驱和后继节点的地址。
双链表对PHP开发程序来讲是很重要的一种数据结构,可以把PHP数组中想想成一个双链表,而PHP内置的SplDoublyLinkedList类通过实现迭代器、数组访问和获取数量的接口使程序访问对象变得访问数组一样方便。
SplDoublyLinkedList类代码如下:<?php/*** PS:关于预定义接口Iterator, ArrayAccess, Countable的文章已经介绍过了,不认识的可以往前翻翻* @link /wuxing26jiayou/article/details/51862707*/class SplDoublyLinkedList implements Iterator, ArrayAccess, Countable{/*** @var _llist 定义一个数组用于存放数据*/protected $_llist = array();/*** @var _it_mode 链表的迭代模式*/protected $_it_mode = 0;/*** @var _it_pos 链表指针*/protected $_it_pos = 0;/*** 迭代模式* @see setIteratorMode*/const IT_MODE_LIFO = 0x00000002;const IT_MODE_FIFO = 0x00000000;const IT_MODE_KEEP = 0x00000000;const IT_MODE_DELETE = 0x00000001;/*** @return 返回被移出尾部节点元素* @throw RuntimeException 如果链表为空则抛出异常*/public function pop(){if (count($this->_llist) == 0) {throw new RuntimeException("Can't pop from an empty datastructure");}return array_pop($this->_llist);}/*** @return 返回被移出头部节点元素* @throw RuntimeException 如果链表为空则抛出异常*/public function shift(){if (count($this->_llist) == 0) {throw new RuntimeException("Can't shift from an empty datastructure");}return array_shift($this->_llist);}/*** 往链表尾部添加一个节点元素* @param $data 要添加的节点元素*/public function push($data){array_push($this->_llist, $data);return true;}/*** 往链表头部添加一个节点元素* @param $data 要添加的节点元素*/public function unshift($data){array_unshift($this->_llist, $data);return true;}/*** @return 返回尾部节点元素,并把指针指向尾部节点元素*/public function top(){return end($this->_llist);}/*** @return 返回头部节点元素,并把指针指向头部节点元素*/public function bottom(){return reset($this->_llist);}/*** @return 返回链表节点数*/public function count(){return count($this->_llist);}/*** @return 判断链表是否为空*/public function isEmpty(){return ($this->count() == 0);}/*** 设置迭代模式* - 迭代的顺序(先进先出、后进先出)* - SplDoublyLnkedList::IT_MODE_LIFO (堆栈)* - SplDoublyLnkedList::IT_MODE_FIFO (队列)** - 迭代过程中迭代器的行为* - SplDoublyLnkedList::IT_MODE_DELETE (删除已迭代的节点元素)* - SplDoublyLnkedList::IT_MODE_KEEP (保留已迭代的节点元素)** 默认的模式是0 : SplDoublyLnkedList::IT_MODE_FIFO | SplDoublyLnkedList::IT_MODE_KEEP** @param $mode 新的迭代模式*/public function setIteratorMode($mode){$this->_it_mode = $mode;}/*** @return 返回当前的迭代模式* @see setIteratorMode*/public function getIteratorMode(){return $this->_it_mode;}/*** 重置节点指针*/public function rewind(){if ($this->_it_mode & self::IT_MODE_LIFO) {$this->_it_pos = count($this->_llist)-1;} else {$this->_it_pos = 0;}}/*** @return 判断指针对应的节点元素是否存在*/public function valid(){return array_key_exists($this->_it_pos, $this->_llist); }/*** @return 返回当前指针的偏移位置*/public function key(){return $this->_it_pos;}/*** @return 返回当前指针对应的节点元素*/public function current(){return $this->_llist[$this->_it_pos];}/*** 将指针向前移动一个偏移位置*/public function next(){if ($this->_it_mode & self::IT_MODE_LIFO) {if ($this->_it_mode & self::IT_MODE_DELETE) {$this->pop();}$this->_it_pos--;} else {if ($this->_it_mode & self::IT_MODE_DELETE) {$this->shift();} else {$this->_it_pos++;}}}/*** @return 偏移位置是否存在** @param $offset 偏移位置* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常*/public function offsetExists($offset){if (!is_numeric($offset)) {throw new OutOfRangeException("Offset invalid or out of range");} else {return array_key_exists($offset, $this->_llist);}}/*** @return 获取偏移位置对应的值** @param $offset 偏移位置* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常*/public function offsetGet($offset){if ($this->_it_mode & self::IT_MODE_LIFO) {$realOffset = count($this->_llist)-$offset;} else {$realOffset = $offset;}if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) { throw new OutOfRangeException("Offset invalid or out of range");} else {return $this->_llist[$realOffset];}}/*** @return 设置偏移位置对应的值** @param $offset 偏移位置* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常*/public function offsetSet($offset, $value){if ($offset === null) {return $this->push($value);}if ($this->_it_mode & self::IT_MODE_LIFO) {$realOffset = count($this->_llist)-$offset;} else {$realOffset = $offset;}if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) { throw new OutOfRangeException("Offset invalid or out of range");} else {$this->_llist[$realOffset] = $value;}}/*** @return 删除偏移位置对应的值** @param $offset 偏移位置* @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常*/public function offsetUnset($offset){if ($this->_it_mode & self::IT_MODE_LIFO) {$realOffset = count($this->_llist)-$offset;} else {$realOffset = $offset;}if (!is_numeric($offset) || !array_key_exists($realOffset, $this->_llist)) { throw new OutOfRangeException("Offset invalid or out of range");} else {array_splice($this->_llist, $realOffset, 1);}}}?>。
C++课件 简单链表及其应用
总结和参考资料
1 总结
链表是一种灵活的数据结构,可用于解决各种问题。理解链表的实现原理和应用场景对 于编程非常重要。
2 参考资料
1. 数据结构与算法分析 (C++语言描述) 2. 算法导论 (原书第三版)
尾节点
链表的最后一个节点被称为尾节点,它包含 数据并指向NULL。
删除节点
在单向链表中,删除节点需要修改前一个节 点的指针,使其指向下一个节点。
双向链表的实现原理
前向指针
每个节点除了后向指针,还有一个指向前一个节 点的前向指针。
后向指针
每个节点除了前向指针,还有一个指向后一个节 点的后向指针。
应用场景:LRU Cache的实现
1
LRU Cache
LRU Cache是一种常见的缓存算法,使用链表来存储数据。最近使用的数据位 于链表的头部,最久未使用的数据位于链表的尾部。
2
插入数据
当新的数据被访问时,可以将其插入到链表的头部。如果链表已满,则删除尾部 的节点。
3
பைடு நூலகம்
查询数据
当数据被访问时,可以将其从当前位置移动到链表的头部。
应用场景:判断链表是否存在环
1 快慢指针
2 时间复杂度
使用两个指针,一个快指针和一个慢指针, 从链表的头部开始遍历。如果链表存在环, 快指针会在某个时刻追上慢指针。
这种方法的时间复杂度是O(n),其中n是 链表的长度。
常见问题及解决方案
内存泄漏
确保在删除节点时释放相关内存。
链表长度限制
通过设置最大长度或使用动态扩展的数据结构 来解决。
C++课件 简单链表及其应 用
本课件将介绍链表的定义和基本操作,单向链表的实现原理,双向链表的实 现原理,以及链表在LRU Cache的实现和判断链表是否存在环的应用场景。
数据结构之链表篇(单链表,循环链表,双向链表)C语言版
数据结构之链表篇(单链表,循环链表,双向链表)C语⾔版1.链表 链表是线性表的⼀种,由⼀系列节点(结点)组成,每个节点包含⼀个数据域和⼀个指向下⼀个节点的指针域。
链表结构可以克服数组需要预先知道数据⼤⼩的缺点,⽽且插⼊和删除元素很⽅便,但是失去数组随机读取的优点。
链表有很多种不同类型:单向链表,双向链表和循环链表。
在链表中第⼀个节点叫头节点(如果有头节点)头节点不存放有效信息,是为了⽅便链表的删除和插⼊操作,第⼀个有效节点叫⾸节点,最后⼀个节点叫尾节点。
2.单链表的操作 链表的操作⼀般有创建链表,插⼊节点,删除节点,遍历链表。
插⼊节点的⽅法有头插法和尾插法,头插法是在头部插⼊,尾插法是在尾部插⼊。
下⾯以⼀个带头节点,采⽤尾插法的链表说明链表的各种操作。
1 #include<stdio.h>2 #include<stdlib.h>3//单链表456//节点结构体7 typedef struct node8 {9int value;//数据域10struct node*next;//指针域11 }Node;1213 Node*createList();//创建链表并且返回头节点指针14void deleteNode(Node*head);//删除节点15void insertNode(Node*head);//插⼊节点16void travelList(Node*head);//遍历链表1718int main()19 {20 Node*head=createList();21 travelList(head);22 insertNode(head);23 travelList(head);24 deleteNode(head);25 travelList(head);26return0;27 }28//创建链表,返回头节点指针29 Node*createList()30 {31//采⽤尾插法32 Node*head;//头节点33 Node*tail;//尾节点34 Node*temp=NULL;35int i,value,size;36 head=(Node*)malloc(sizeof(Node));//头节点37 head->value=0;38 head->next=NULL;39 tail=head;40 printf("输⼊节点个数: ");41 scanf("%d",&size);42 printf("输⼊各个节点的值: ");4344for(i=0;i<size;i++)45 {46 scanf("%d",&value);47 temp=(Node*)malloc(sizeof(Node));48 temp->value=value;49 tail->next=temp;//让尾节点的指针域指向新创建的节点50 tail=temp;//尾节点改为新创建的节点51 tail->next=NULL;//让尾节点的指针域为空52 }53return head;54 }55//遍历链表56void travelList(Node*head)57 {58while(head->next!=NULL)59 {60 printf("%d\n",head->next->value);61 head=head->next;62 }63 }64//插⼊节点65void insertNode(Node*head)66 {67int value;68int position;69int pos=0;70 Node*pre=NULL;//⽤来保存要插⼊节点的前⼀个节点71 Node*newNode;72 printf("输⼊要插⼊节点的值: ");73 scanf("%d",&value);74 printf("要插⼊的位置: ");75 scanf("%d",&position);76while(head!=NULL)77 {78 pos++;79 pre=head;80 head=head->next;81if(pos==position)82 {83 newNode=(Node*)malloc(sizeof(Node));84 newNode->value=value;85 newNode->next=pre->next;86 pre->next=newNode;87 }88 }89 }90//删除节点91void deleteNode(Node*head)92 {93int value;94 Node*pre=head;95 Node*current=head->next;96 printf("输⼊要删除节点的值: ");97 scanf("%d",&value);98while(current!=NULL)99 {100if(current->value==value)101 {102 pre->next=current->next;103free(current);//释放空间104break;105 }106 pre=current;107 current=current->next;108 }109 }3.循环链表 循环链表就是让尾节点的指针域不再是NULL,⽽是指向头节点从⽽形成⼀个环。
《数据结构与算法》课件 第3章 链表
练习
1、链表中逻辑上相邻的元素在物理上()相邻。 2、已知带头结点的单链表L,指针p指向链表中的一个节点, 指针q指向链表外的节点,在指针p的后面插入q的语句序 列( ) 3、设某非空单链表,要删除指针p所指的结点的直接后继结 点,则需要执行下述语句序列: p=q->next; ( );free(p); 4、线性表的存储有顺序存储和( )存储两种。 5、线性表中哪些元素只有一个直接前驱和一个直接后继? A 首元素 b 尾元素 c 中间的元素 d 所有的元素 6、线性表的各元素之间是()关系 A 层次 b 网状 c 有序 d 集合 7、在单链表中一个结点有()个指针,在双向链表中的一 个结点有()指针
2、求长度 L 21 18 p k p
30
p
75
p
42
p
56 ∧
p p
6 5 4 3 2 1 0
int list_length(LinkList L) {int n=0; LinkList p=L->next; while(p!=NULL) { n++;p=p->next;} return n; }
exit(0);}
s=(SNode *) malloc(sizeof(SNode)); sdata=x; snext=prenext; prenext=s; }
5、删除算法的实现
void LinkListDelete(LinkList L,int i)
……..
ai-1
ai
ai+1
……..
P
相互之间的关系是靠其中的后继地址来表示的
动态链表:根据实际需要临时分配
结构描述如下: typedef struct SNode{ ElemType data; struct SNode *next; //指向结构体类型指针 }*LinkList;
C#数据结构之双向链表(DbLinkList)实例详解
C#数据结构之双向链表(DbLinkList)实例详解本⽂实例讲述了C#数据结构之双向链表(DbLinkList)。
分享给⼤家供⼤家参考,具体如下:这是继上⼀篇《》的继续,对于双向链接,节点上除了Next属性外,还要有Prev属性⽤来指向前⼀个节点,DbNode定义如下:namespace 线性表{public class DbNode<T>{private T data;private DbNode<T> prev;private DbNode<T> next;public DbNode(T data, DbNode<T> next,DbNode<T> prev){this.data = data;this.next = next;this.prev = prev;}public DbNode(T data, DbNode<T> next){this.data = data;this.next = next;this.prev = null;}public DbNode(DbNode<T> next){this.data = default(T);this.next = next;this.prev = null;}public DbNode(T data){this.data = data;this.next = null;this.prev = null;}public DbNode(){data = default(T);next = null;prev = null;}public T Data{set { this.data = value; }get { return this.data; }}public DbNode<T> Prev{get { return prev; }set { prev = value; }}public DbNode<T> Next{get { return next; }set { next = value; }}}}双链表的插⼊操作要稍微复杂⼀点,⽰意图如下:同样对于删除操作,也要额外处理prev指向完整实现DbLinkList<T>:namespace 线性表{public class DbLinkList<T> : IListDS<T> {private DbNode<T> head;public DbNode<T> Head{get { return head; }set { head = value; }}public DbLinkList(){head = null;}/// <summary>/// 类索引器/// </summary>/// <param name="index"></param>/// <returns></returns>public T this[int index]{get{return this.GetItemAt(index);}}/// <summary>/// 返回单链表的长度/// </summary>/// <returns></returns>public int Count(){DbNode<T> p = head;int len = 0;while (p != null){len++;p = p.Next;}return len;}/// <summary>/// 清空/// </summary>public void Clear(){head = null;}/// <summary>/// 是否为空/// </summary>/// <returns></returns>public bool IsEmpty(){return head == null;}/// <summary>/// 在最后附加元素/// </summary>/// <param name="item"></param>public void Append(T item){DbNode<T> d = new DbNode<T>(item); DbNode<T> n = new DbNode<T>();if (head == null){head = d;return;}n = head;while (n.Next != null){n = n.Next;}n.Next = d;//前插public void InsertBefore(T item, int i){if (IsEmpty() || i < 0){Console.WriteLine("List is empty or Position is error!"); return;}//在最开头插⼊if (i == 0){DbNode<T> q = new DbNode<T>(item);q.Next = head;//把"头"改成第⼆个元素head.Prev = q;head = q;//把⾃⼰设置为"头"return;}DbNode<T> n = head;DbNode<T> d = new DbNode<T>();int j = 0;//找到位置i的前⼀个元素dwhile (n.Next != null && j < i){d = n;n = n.Next;j++;}if (n.Next == null) //说明是在最后节点插⼊(即追加){DbNode<T> q = new DbNode<T>(item);n.Next = q;q.Prev = n;q.Next = null;}else{if (j == i){DbNode<T> q = new DbNode<T>(item);d.Next = q;q.Prev = d;q.Next = n;n.Prev = q;}}}/// <summary>/// 在位置i后插⼊元素item/// </summary>/// <param name="item"></param>/// <param name="i"></param>public void InsertAfter(T item, int i){if (IsEmpty() || i < 0){Console.WriteLine("List is empty or Position is error!"); return;}if (i == 0){DbNode<T> q = new DbNode<T>(item);q.Next = head.Next;head.Next.Prev = q;head.Next = q;q.Prev = head;return;}DbNode<T> p = head;int j = 0;while (p != null && j < i){p = p.Next;j++;}DbNode<T> q = new DbNode<T>(item);q.Next = p.Next;if (p.Next != null){p.Next.Prev = q;}p.Next = q;q.Prev = p;}else{Console.WriteLine("Position is error!");}}/// <summary>/// 删除位置i的元素/// </summary>/// <param name="i"></param>/// <returns></returns>public T RemoveAt(int i){if (IsEmpty() || i < 0){Console.WriteLine("Link is empty or Position is error!"); return default(T);}DbNode<T> q = new DbNode<T>();if (i == 0){q = head;head = head.Next;head.Prev = null;return q.Data;}DbNode<T> p = head;int j = 0;while (p.Next != null && j < i){j++;q = p;p = p.Next;}if (j == i){p.Next.Prev = q;q.Next = p.Next;return p.Data;}else{Console.WriteLine("The node is not exist!");return default(T);}}/// <summary>/// 获取指定位置的元素/// </summary>/// <param name="i"></param>/// <returns></returns>public T GetItemAt(int i){if (IsEmpty()){Console.WriteLine("List is empty!");return default(T);}DbNode<T> p = new DbNode<T>();p = head;if (i == 0){return p.Data;}int j = 0;while (p.Next != null && j < i)j++;p = p.Next;}if (j == i){return p.Data;}else{Console.WriteLine("The node is not exist!");return default(T);}}//按元素值查找索引public int IndexOf(T value){if (IsEmpty()){Console.WriteLine("List is Empty!");return -1;}DbNode<T> p = new DbNode<T>();p = head;int i = 0;while (!p.Data.Equals(value) && p.Next != null){p = p.Next;i++;}return i;}/// <summary>/// 元素反转/// </summary>public void Reverse(){DbLinkList<T> result = new DbLinkList<T>();DbNode<T> t = this.head;result.Head = new DbNode<T>(t.Data);t = t.Next;//(把当前链接的元素从head开始遍历,逐个插⼊到另⼀个空链表中,这样得到的新链表正好元素顺序跟原链表是相反的) while (t!=null){result.InsertBefore(t.Data, 0);t = t.Next;}this.head = result.head;//将原链表直接挂到"反转后的链表"上result = null;//显式清空原链表的引⽤,以便让GC能直接回收}//得到某个指定的节点(为了下⾯测试从后向前遍历)private DbNode<T> GetNodeAt(int i){if (IsEmpty()){Console.WriteLine("List is empty!");return null;}DbNode<T> p = new DbNode<T>();p = head;if (i == 0){return p;}int j = 0;while (p.Next != null && j < i){j++;p = p.Next;}if (j == i){return p;}else{Console.WriteLine("The node is not exist!");}}/// <summary>/// 测试⽤prev属性从后⾯开始遍历/// </summary>/// <returns></returns>public string TestPrevErgodic(){DbNode<T> tail = GetNodeAt(Count() - 1);StringBuilder sb = new StringBuilder();sb.Append(tail.Data.ToString() + ",");while (tail.Prev != null){sb.Append(tail.Prev.Data.ToString() + ",");tail = tail.Prev;}return sb.ToString().TrimEnd(',');}public override string ToString(){StringBuilder sb = new StringBuilder();DbNode<T> n = this.head;sb.Append(n.Data.ToString() + ",");while (n.Next != null){sb.Append(n.Next.Data.ToString() + ",");n = n.Next;}return sb.ToString().TrimEnd(',');}}}测试代码⽚段:Console.WriteLine("-------------------------------------");Console.WriteLine("双链表测试开始...");DbLinkList<string> dblink = new DbLinkList<string>();dblink.Head = new DbNode<string>("x");dblink.InsertBefore("w", 0);dblink.InsertBefore("v", 0);dblink.Append("y");dblink.InsertBefore("z", dblink.Count());Console.WriteLine(dblink.Count());//5Console.WriteLine(dblink.ToString());//v,w,x,y,zConsole.WriteLine(dblink[1]);//wConsole.WriteLine(dblink[0]);//vConsole.WriteLine(dblink[4]);//zConsole.WriteLine(dblink.IndexOf("z"));//4Console.WriteLine(dblink.RemoveAt(2));//xConsole.WriteLine(dblink.ToString());//v,w,y,zdblink.InsertBefore("x", 2);Console.WriteLine(dblink.ToString());//v,w,x,y,zConsole.WriteLine(dblink.GetItemAt(2));//xdblink.Reverse();Console.WriteLine(dblink.ToString());//z,y,x,w,vdblink.InsertAfter("1", 0);dblink.InsertAfter("2", 1);dblink.InsertAfter("6", 5);dblink.InsertAfter("8", 7);dblink.InsertAfter("A", 10);//Position is error!Console.WriteLine(dblink.ToString()); //z,1,2,y,x,w,6,v,8string _tail = dblink.GetItemAt(dblink.Count()-1);Console.WriteLine(_tail);Console.WriteLine(dblink.TestPrevErgodic());//8Console.ReadKey(); //8,v,6,w,x,y,2,1,z当然从上⾯的测试代码中,似乎并不能看出双链表的优点,双链表的好处在于,如果需要在链表中,需要通过某个节点得到它的前驱节点时,双链表直接⽤prev属性就能找到;⽽单链表要做到这⼀点,必须再次从Head节点开始⼀个⼀个⽤Next向下找,这样时间复杂度从O(n)降到O(1),显然更有效率。
循环链表和双向链表
b.head->next = NULL; //此时,b中已只剩第一个结点(头), 为其置空表标志
return k; //返回结果链表中的元素个数
}
为了进一步说明上述程序,举一个程序运行的例子, 其各次循环的运行结果如图5-6所示
p
7 0 3 2 -9 3 1 5
^
(a)A(x)=p5(x)=7+3x2-9x3+x5,进入循环前
该程序不断比较A链和B链中的一对结点的指数值 (称其为当前结点)。开始时A链和B链中参加比较
的当前结点都是它们的第一个元素。
主循环while结束后,可能出现下列3种情况:①A
链和B链同时被处理完;②只有B链处理完;③只有A
链处理完。 对第一和第二种情况,不需要“善后”处理。对第 三种情况,B链中尚有未被处理完的结点,需将其挂 接在结果链的尾部。循环外的“if(q 不为空)将q
p = p->next; } // if (x==0) … else … q0 = q; q = q->next; delete q0; //将q所指结点从表中删除并释放,令q新指向原所 指的下一个 } // if (p->exp > q->exp ) … else … } //while if (q!=NULL) p0->next = q;
为处理方便,在具体存储多项式时,我们规定:
所存储的多项式已约简,即已合并同类项,不 保留0系数项,各项按指数的升序排列。 (二)多项式加法实现—直接操作链表 为操作方便,我采用带头结点的非循环链表,下面给 出一个例子说明多项式的这种表示法。
设有一个一元5次多项式: P5(x)=7+3x-9x3+x5
双链表——精选推荐
双链表双向链表的定义双向链表也是链表的⼀种,它每个数据结点中都有两个结点,分别指向其直接前驱和直接后继。
所以我们从双向链表的任意⼀个结点开始都可以很⽅便的访问其前驱元素和后继元素。
第⼀就是头节点的前驱指针指向NULL空指针。
第⼆就是尾节点的后驱指针指向NULL指针。
双向链表的结构:双向链表的操作1. 创建双链表2. 新增节点(在链表尾插⼊)3. 从头遍历链表4. 判断链表是否为空5. 计算链表长度6. 从链表删除节点7. 删除整个链表8. 获取某个节点对象9. 从⼀个节点向前遍历10. 从⼀个节点向后遍历节点类型定义⼀个节点有⼀个前驱节点、⼀个后驱节点,和数据。
typedef struct doubleLink doubleLinkNode;struct doubleLink{int value ;doubleLinkNode *pre;doubleLinkNode *next;};创建双链表的头节点创建节点,必须使⽤malloc为节点分配内存,头节点不存储数据,且前驱指针为空,此时刚创建双链表,所以头指针的后驱指针也为空。
//创建双链表的头节点doubleLinkNode * create(){doubleLinkNode *head;head = (doubleLinkNode*)malloc(sizeof(doubleLinkNode));head->pre = NULL;head->next =NULL;return head;}新增节点,在链表尾插第⼀条判断语句是判断头指针为NULL时,返回-1,当链表被删除,头指针为NULL,然后新建node节点,它的后驱指针为NULL,它的前驱指针为原来的最后的节点,所以原来最后的节点的后驱指针变为它,它为双链表的最后⼀个节点。
int add(doubleLinkNode *head,int value){if(head == NULL)return -1;doubleLinkNode *t=head;doubleLinkNode *node;node = (doubleLinkNode*)malloc(sizeof(doubleLinkNode));//此节点的后驱指针为NULL,添加后,它为最后⼀个节点node->next =NULL;node->value =value;//找到最后⼀个节点,最后⼀个节点的后驱指针为空while(t->next!=NULL){t=t->next;}//此节点的前驱指针指向原本的最后⼀个节点,它成为最后⼀个节点node->pre = t;//原本最后节点的后驱指针此节点t->next = node;return1;}从头遍历链表最后⼀个指针的NULL为空,这是我们判断的依据,所以判断t->next,直⾄判断到最后⼀个节点,这⾥双链表的头指针是没有数据的,但是直接判断完t->next,然后对t赋值,是不会打印头指针的。
链表(二)
DLinkList *DL, *p;
结点的结构如右图:
prior
data
next
若指针 p 指向双向链表中的某一结点,则有 如下关系: p->next->prior= P
.
p->prior->next=
P
prior
data
next
.
也就是说,结点 p 的后继的前趋指向该结点 本身,同理,结点 p 前趋的后继也指向该结点本 身。有时,双向链表的这一性质可称为双向链表 结构的 对称性 。
Temp
3、插入结点
算法2.12 插入结点(在i个结点后插入) int InsertCList(LinkList *L,int i,ElemType e) {int j; LinkList *temp,*node; node=( LinkList *)malloc(sizeof(LinkList)); if(node==NULL) return FALSE; /*申请结点空间失败*/ temp=L->next; j=1; while((j<i) && temp!=L) { j++; temp=temp->next;} /*寻找插入位置*/ node->data=e; node->next=temp->next; temp->next=node; /*插入结点*/ return TRUE; 本算法时间复杂度为O(n),其中n是链表的长度。 }
思考:如何证明一个链表是循环链表?
分析:若为循环链表,则在遍历该链表时,其不会停止, 且每个结点都会被重复访问。
策略:可以设置两个步长不等的移动指针(如一个步长为1, 另一个步长为2),两指针同时出发,它们若能再次相遇即 可证明该链表是循环链表。
linux内核分析之list_head双向链表结构.doc
linux 内核分析之list_head 双向链表结构本文详细分析了 2.6.x 内核中链表结构的实现,并通过实例对每个链表操作接口进行了详尽的讲解。
一、链表数据结构简介链表是一种常用的组织有序数据的数据结构,它通过指针将一系列数据节点连接成一条数据链,是线性表的一种重要实现方式。
相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或删除数据。
链表的开销主要是访问的顺序性和组织链的空间损失。
通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系。
按照指针域的组织以及各个节点之间的联系形式,链表又可以分为单链表、双链表、循环链表等多种类型,下面分别给出这几类常见链表类型的示意图:1.单链表图1 单链表单链表是最简单的一类链表,它的特点是仅有一个指针域指向后继节点(next),因此,对单链表的遍历只能从头至尾(通常是NULL空指针)顺序进行。
2.双链表图2 双链表通过设计前驱和后继两个指针域,双链表可以从两个方向遍历,这是它区别于单链表的地方。
如果打乱前驱、后继的依赖关系,就可以构成"二叉树";如果再让首节点的前驱指向链表尾节点、尾节点的后继指向首节点(如图2中虚线部分),就构成了循环链表;如果设计更多的指针域,就可以构成各种复杂的树状数据结构。
3.循环链表循环链表的特点是尾节点的后继指向首节点。
前面已经给出了双循环链表的示意图,它的特点是从任意一个节点出发,沿两个方向的任何一个,都能找到链表中的任意一个数据。
如果去掉前驱指针,就是单循环链表。
在Linux内核中使用了大量的链表结构来组织数据,包括设备列表以及各种功能模块中的数据组织。
这些链表大多采用在[include/linux/list.h]实现的一个相当精彩的链表数据结构。
本文的后继部分就将通过示例详细介绍这一数据结构的组织和使用。
双向循环链表操作-二叉树和树操作-图的创建及相关操作的实现9
一、双向循环链表 结构
begin
A
BCend来自 增加节点CA
B
删除节点
A
B
C
就地逆置
A
B
C
D
E
实现功能
(2)使用孩子-兄弟表示法作为存储结构,实现树 的先根、后根遍历和层次遍历;
(3)使用孩子-兄弟表示法作为存储结构,统计树 中叶子结点的个数;
三、图 结构
A
B
E
C
D
存储结构
实现功能
(10)对于图(不是网),求顶点u到 v的所有简单路径; (11)实现Dijkstra和Floyd算法求最 短路径; (12)实现普里姆或克鲁斯卡尔算法求 最小生成树。
谢谢观赏
谢谢观赏
双向循环链表操作-二叉树和树操作-图的创建及相关操作的实现9
选做题目
1、双向循环链表 2、树:
(2)使用孩子-兄弟表示法作为存储结构,实现树的先根、 后根遍历和层次遍历;
(3)使用孩子-兄弟表示法作为存储结构,统计树中叶子 结点的个数;
(4)使用双亲表示法作为存储结构,统计树的深度。 3、图:
(10)对于图(不是网),求顶点u到v的所有简单路径; (11)实现Dijkstra和Floyd算法求最短路径; (12)实现普里姆或克鲁斯卡尔算法求最小生成树。
链图
55定义线性链表的数据类型56双向链表删除57双向链表的插入58二叉链表的定义59需用到栈,顺序栈的定义如下64孩子兄弟表示法65队列的定义66队列的删除1线形表的插入2线形表的删除3单链表的查找4单链表的插入5单链表的删除6入栈7出栈8链栈的入栈9链栈的出栈10汉诺塔11入队12出队13划分子集55定义线性链表的数据类型typedef struct LNode {ElemType data;struct LNode *next;}LNode,*LinkList56双向链表删除Status ListDelete_Dul(DuLinkList &L,int i,ElemType &e){if (! (p=GetElemP_Dul(L,i))) return ERROR;e =p->data;p->prior->next= p->next;p->next->prior= p->prior;free( p );return OK}57双向链表的插入Status ListInsert_Dul(DuLinkList &L,int i,ElemType e){if (! (p=GetElemP_Dul(L,i))) return ERROR;if (! (s= (DuLinkList)malloc(sizeof(DuLNode)))) return ERRORs->data=e;s->prior=p->prior; p->prior->next=s;s->next=p;p->prior=s;return OK}58二叉链表的定义typedef struct BiTNode{TElemType data;Struct BiTNode *lchild,*rchild;}BiTNode, *BiTree;59需用到栈,顺序栈的定义如下:typedef BiTNode* SElemType;typedef struct{SElemType *base;SElemType *top;int stacksize;}SqStack;64孩子兄弟表示法typedef struct CSNode{ElemType data;struct CSNode *firstchild, *nextsibling;}CSNode,*CSTree;65队列的定义typedef struct QNode{ QElemType data;struct QNode *next;}QNode,*QueuePtr;Typedef struct{QueuePtr front;QueuePtr rear;}LinkQueue;66队列的删除Status Dequeue(LinkQueue &Q,QelemType &e){ If(Q.front==Q.rear)return ERROR;P=Q.front->next;e=p->data;Q.front->next=p->nextIf(Q.rear==p)Q.rear=q.front;Free(P);Renturn OK;1线形表的插入int sxbcr(int i,int x,int v[],int *p){int j,n;n=*p;if((i<1) || (i>n+1))return (0);else{ for(j=n;j>=i;j--)v[j]=v[j-1];v[j]=x;*p=++n;return (1);}}2线形表的删除int sxbsc(int i,int v[],int *p){int j,n;n=*p;if((i<1) || (i>n))return (0);else{ for(j=i;j<n;j++)v[j-1]=v[j];*p=--n;return (1);}}3单链表的查找LNode *dlbcz(LNode *L,int X){ LNode *p;p=L;while(p!=NULL && p->data!=X)p=p->next;return (p);}4单链表的插入void dlbcr(LNode *p,int x){ LNode *s;s=(LNode *)malloc(sizeof(LNode));s->data=x;s->next=p->next;p->next=s;5单链表的删除void dlbsc(LNode *p){ LNode *q;if(p->next!=NULL){q=p->next;p->next=q->next;free(q);}}}LNode * dlbjl(int a[],int n){LinkList *s,*h;int i;h=(LNode *)malloc(sizeof(LNode));h->data=0;h->next=NULL;for(i=n;i>0;i--){s=(LNode *)malloc(sizeof(LNode));s->data=a[i-1];s->next=h->next;h->next=s;}return (h);}6入栈int push(SqStack &S,SElemType e)//插入元素e为新的棧顶元素{if(S.top-S.base>=S.stacksize){S.base=(ElemType*)realloc(S.base,(S.stacksize+STACKINCREAMENT)*sizeof(ElemType));if(!S.base) exit(OVERFLOW);S.top=S.base+S.stacksize;S.stacksize+=STACKINCREAMENT;}*S.top++=e;return OK;}7出栈int Pop(SqStack &S,SElemType &e){if(S.top==S.base) return ERROR;e=*--S.top;return OK;}8链栈的入栈QNode *lzjz(QNode *top,int x){ QNode *p;p=(QNode *)malloc(sizeof(QNode));p->data=x;p->next=top;top=p;return(p);}9链栈的出栈JD *lztz(JD *top,int *p){ JD *q;if(top!=NULL){ q=top;*p=top->data;top=top->link;free(q);}return(top);}10汉诺塔/*Hanoi.txt*/main(){ int m;printf("Input the number of disks:");scanf("%d",&m);printf("The steps to moving %3d disks:\n",m);hanoi(m,'A','B','C');(0) }void hanoi(int n,char x,char y,char z)(1) {(2) if(n==1)(3) move(1,x,z);(4) else{(5) hanoi(n-1,x,z,y);(6) move(n,x,z);(7) hanoi(n-1,y,x,z);(8) }(9) }void move(int h,char c,char f){printf("%d:%c--->%c\n",h,c,f);}11入队QNode *dlcr(QNode *rear,int x){ QNode *p;p=(QNode *)malloc(sizeof(QNode));p->data=x;p->next=NULL;rear->next=p;return(p);}12出队int dlsc(QNode *front,QNode *rear){ QNode *s;int x;if(front==rear)return(-1);s=front->next;front->next=s->next;if(s->next==NULL)rear=front;x=s->data;free(s);return(x);}13划分子集void division(int r[][N],int n,int cq[],int newr[],int result[]){ int k,i,pre,group;for(k=0;k<n;k++)cq[k]=k+1;front=n-1;rear=n-1;for(k=0;k<n;k++)newr[k]=0;group=1;pre=0;do{ front=(front+1)%n;i=cq[front];if(i<pre){ group++;result[i-1]=group;for(k=0;k<n;k++)newr[k]=r[i-1][k];}else if(newr[i-1]!=0){ rear=(rear+1)%n;cq[rear]=i;}else{ result[i-1]=group;for(k=0;k<n;k++)newr[k]=newr[k]+r[i-1][k];}pre=i;}while(rear!=front);。