一、单链表
单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为节点列表,因为链表是由一个个节点组装起来的;其中每个节点都有指针成员变量指列表中的下一个节点;
列表是由节点构成,由head指针指向第一个成为表头的节点而终止于最后一个指向nuLL的指针;
1.建立链表(尾插法):
尾插法的基本思想:
1.生成一个节点,将读入的数据存放到新节点的数据域中
2.把新节点插到当前链表的尾节点之后
3.重复上述过程,直至输入结束标志
尾插法读入数据的顺序和线性表的逻辑顺序是相同的
void creat_list( Node* &head) { //传入指向表头的指针,这个指针要保存下来。 注意Node* &, 是Node* 类型的引用
Node *pre,*newNode;
head = pre = new Node; //生成表头结点
newNode = new Node; //建立新节点
cin>>newNode->num; //输入新节点的值
while(newNode->num) { //判断输入结束的条件
pre-> next = newNode; //上一个节点连接新节点
pre = newNode; //因为马上要申请新空间,此时的新节点即将变成“上一个节点”,保存给pre
newNode = new Node; //再次申请新空间
cin >> newNode->num; //输入值
}
pre->next = NULL; //结束后,将单链表最后一个节点rear指针置为空,标志着结束
}
2.建立链表(头插法)
头插法的基本思想:
1.生成一个节点,将读入的数据存放到新节点的数据域中
2.把新节点作为第一个表节点查到单签链表头节点之后
3.重复上诉过程,直至输入结束标志为止
头插法读入数据的顺序和线性表的逻辑顺序是相反的。
void creat_head(Node* &head) {
Node *newNode;
head = new Node;
head->next = NULL;
newNode = new Node;
cin>>newNode->num; //输入数据
while(newNode->num){
newNode->next = head->next;
head->next = newNode;
newNode = new Node;
cin >> newNode->num;
}
}
3.单链表查找:按值查找
Node* search_value(Node *head, int x){ //x为要查找的值
Node *p=head->next;
while(p!=NULL && p->num!=x)
p = p->next;
if(p->num ==x )
return (p);
return NULL;
}
4.单链表查找:按位置查找
Node* search_local(Node *head, int i) { //i为要查找的位置
Node *p=head;
int count=0;
while(p->next!=NULL && count<i) {
p = p->next;
++count;
}
if(count==i)
return (p);
return NULL;
}
5.单链表插入:前插(后插)
操作过程:
1.生成一个新节点newNode,将值x赋给新节点newNode的数据域
2.从表头节点开始,查找p的前驱节点q
3.修改有关节点的指针域,将newNode指针域指向p节点,q节点指向newNode
Node* inset_front(Node *head,int i,int x){ //x为要插入的值,i是位置
Node *p,*newNode;
newNode = new Node; //申请新空间
newNode->num = x; //赋值
p = search_local(head,i-1); //只要把i-1改成i,就变成了插在i的后面(后插法)
newNode->next = p->next;
p->next = newNode;
return head;
}
6.单链表的删除(删除链表中的值x)
操作过程:
1.假设有两个节点pre和p,pre是p的前驱节点,p为要删除的节点
2.找pre,从链表head的头结点开始,一次向后进行搜索,当pre-next = p时,找到
3.修改pre-next = p-next,把p的前驱节点指向p的后继节点,并且删除空间
Node* delete_link(Node *head,int x){
Node *pre,*p; //pre存p的前驱节点
p = head;
while(p!=NULL && p->num!=x) {
pre=p;
p = p->next;
}
if(p!=NULL) {
pre->next = p->next;
delete (p);
return head;
}
else {
cout<<"要删除的值不存在\n";
return NULL;
}
}
二、循环列表
循环链表:表中最后一个节点的指针域不再是空,而是指向第一个节点,整个链表形成一个环。
特点:从表中任一节点出发,就可搜寻到其他所有节点
作用:在许多实际应用中,对链表的操作运算是在表头、表尾进行的,这时可以改变链表的标识方法,不用头指针
而用指向尾节点的尾指针rear来标识,以提高效率。
和单链表的比较:
单链表中,找到第一个元素时间为O(1),最后一个元素为O(n);
而应用循环链表,则找到最后一个元素为O(1),由于最后一个元素指向第一个元素,因此找到第一个元素也为O(1)
运算: 循环链表与单链表的运算基本一致,区别在于判断遍历表完成条件不同,单链表判断当前指针域是否为空,而循环
链表判断当前节点指针域是否为表头指针。
//下面以查找运算为例
//下面以查找运算为例
Node* search_value(Node *head, int x){ //x为要查找的值
Node *p=head->next;
while(p->next!=head && p->num!=x) // p->next!=head
p = p->next;
if(p->num ==x )
return (p);
return NULL;
}
三、双向链表
双向链表:指沿着前驱和后继方向都能遍历的线性链表。
其每个节点都有两个指针域:一个指向其后继节点,另一个指向其前驱结点。
双向链表的节点类型定义为:
struct Dnode{
int data; //data储存表中的元素
Dnode *next,*prior; //next指向后一节点指针,prior指向前一节点指针
};
双向链表优点: 双向链表既有向前的指针域,又有向后的指针域,这就使得双向链表的前插和后插
以及删除操作都很方便,只需要修改几个节点的指针域,而不必进行大量的数据交换和遍历操作
// 节点类型
struct Dnode{
int data; //data储存表中的元素
Dnode *next,*prior; //next指向后一节点指针,prior指向前一节点指针
};
//双向链表的前插(在某节点p之前插入一个新节点s)
Dnode *insert_before(Dnode *head,Dnode *p, int x){
Dnode *s; //s为要插入的节点
s = new Dnode; //建立新节点
s->data = x; //给s节点的数据域赋值
s->next = p; //步骤1:将节点s的后继指针指向p;
s->prior = p->prior; //步骤2:将节点s的前驱指针指向节点p的前驱节点
p->prior->next = s; //步骤3:将p的前驱结点的后继指针修改为指向新节点s;
p->prior = s; //步骤4:将节点p的前驱指针修改为指向新节点s.
return head;
}
//双向链表的后插(在某节点p之后插入一个新节点s):
Dnode *insert_back(Dnode *head,Dnode *p, int x){
Dnode *s; //s为要插入的节点
s = new Dnode; //建立新节点
s->data = x; //给s节点的数据域赋值
s->next = p->next; //步骤1:将节点s的后继指针指向p;
s->prior = p; //步骤2:将节点s的前驱指针指向节点p的前驱节点
p->next->prior = s; //步骤3:将p的前驱结点的后继指针修改为指向新节点s;
p->next = s; //步骤4:将节点p的前驱指针修改为指向新节点s.
return head;
}
//双向链表节点的删除:
Dnode *delete_Dnode(Dnode *head,Dnode *p){
if(p != NULL){
p->prior->next = p->next; //步骤1:将p的前驱节点的后继指针改为指向p的后继
p->next->prior = p->prior; //步骤2:将p的后继结点的前驱指针改为指向p的前驱
delete (p); //步骤3:释放p的存储空间
return head; //返回带头结点双向链表的头指针
}
else
return NULL;
}
—— 生命的意义,在于赋予它意义。