单链表,循环链表和双向链表

本文详细介绍了单链表、循环链表及双向链表的特点与实现方法。对比了单链表与顺序表的区别,并阐述了单链表中附加表头的作用。此外,还提供了具体的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单链表:
  一.单链表与顺序表相比:

   1.顺序表可以方便的随机存取表中的任一节点,速度快;但是在表中插入删除一个数据时,为了保持其他元素的相对次序不变,平均需要移动一半的元素,效率很低;还有若事先对表长估计不足,过小会形成内存浪费,过大则需要拷贝到一个更大的数组,时间开销很大。

   2.相反,链表则适用于插入删除频繁,表长估计不定的情形。

   3.单链表中的逻辑位置连续,物理位置非连续;而顺序表都连续。

   二.为什么要带附加表头?

    因为不带附加表头在插入删除时要分两种情况:操作节点在表头和不在表头;而带了附加表头便可以对所有节点一视同仁。

复制代码
  1 template<class T>
  2 struct LinkNode{//链表节点 
  3     T data;
  4     LinkNode *link;
  5     LinkNode(const T& args,LinkNode<T> *ptr=NULL){
  6         data=args;
  7         link=ptr;
  8     }
  9 };
 10 
 11 template<class T>
 12 class List{//带附加头节点的单链表 
 13     protected:
 14         LinkNode<T> *first;//链表头指针 
 15     public:
 16         List(){
 17             first=new LinkNode<T>;
 18         }
 19         List(const T& x){
 20             first=new LinkNode<T>(x);
 21         }
 22         List(List<T>& L){
 23             T value;
 24             LinkNode<T> *L_head=L.getHead();
 25             LinkNode<T> *temp=first=new LinkNode<T>;
 26             while(L_head->link!=NULL){
 27                 value=L_head->link->data;
 28                 temp->link=new LinkNode<T>(value);
 29                 L_head=L_head->link;
 30                 temp=temp->link; 
 31             }
 32             temp->link=NULL;
 33         }
 34         ~List(){
 35             makeEmpty();
 36         }
 37         void makeEmpty(){//将链表置空 
 38             LinkNode<T> *temp;
 39             while(first->link!=NULL){
 40                 temp=first->link;
 41                 first->link=temp->link;
 42                 delete temp;
 43             }
 44         }
 45         int length()const{
 46             LinkNode<T> *temp=first->link;
 47             int count=0;
 48             while(temp!=NULL){
 49                 temp=temp->link;
 50                 count++;
 51             }
 52             return count;
 53         }
 54         LinkNode<T> *getHead()const{//返回附加头节点地址 
 55             return first;
 56         }
 57         LinkNode<T> *search(T x,int& i){//搜索数据x的地址和序列号 
 58             LinkNode<T> *current=first->link;
 59             int index=1;
 60             while(current!=NULL){
 61                 if(current->data==x)
 62                     break;
 63                 current=current->link;
 64                 index++;
 65             } 
 66             i=index;
 67             return current;
 68         }
 69         LinkNode<T> *locate(int i){//搜索第i个节点的地址 
 70             if(i<0)
 71                 return NULL;
 72             LinkNode<T> *current=first;
 73             int temp=0;
 74             while(current!=NULL&&temp<i){
 75                 current=current->link;
 76                 temp++;
 77             }
 78             return current;
 79         }
 80         bool getData(int i,T& x){//取出第i个元素的值 
 81             if(i<=0)
 82                 return false;
 83             LinkNode<T> *current=locate(i);
 84             if(current==NULL) return false;
 85             x=current->data;
 86             return true; 
 87         }
 88         void setData(int i,T& x){//修改第i个元素的值 
 89             if(i<=0)
 90                 return;
 91             LinkNode<T> *current=locate(i);
 92             if(current==NULL) return;
 93             current->data=x;
 94         }
 95         bool insert(int i,T& x){//在第i个元素后插入x 
 96             LinkNode<T> *current=locate(i);
 97             if(current==NULL) return false;
 98             LinkNode<T> *newNode=new LinkNode<T>(x);
 99             newNode->link=current->link;
100             current->link=newNode;
101             return true; 
102         }
103         bool remove(int i,T& x){//删除第i个元素的值 
104             LinkNode<T> *current=locate(i-1);
105             if(current==NULL||current->link==NULL) return false;
106             LinkNode<T> *del=current->link;
107             current->link=del->link;
108             x=del->data;
109             delete del;
110             return true;
111         }
112         bool isEmpty()const{
113             return (first->link==NULL)?true:false;
114         }
115         void inputFront(){//向前插入链表法 
116             makeEmpty();
117             LinkNode<T> *newNode;
118             T value; 
119             cin>>value;
120             while(value!=-1){
121                 newNode=new LinkNode<T>(value);
122                 newNode->link=first->link;
123                 first->link=newNode;
124                 cin>>value;
125             } 
126         }
127         void inputRear(){//向后插入链表法 
128             makeEmpty();
129             LinkNode<T> *newNode,*last=first;
130             T value;
131             cin>>value;
132             while(value!=-1){
133                 newNode=new LinkNode<T>(value);
134                 last->link=newNode;
135                 last=newNode;
136                 cin>>value;
137             }
138         }
139         void output(){//输出整个链表 
140             LinkNode<T> *current=first->link;
141             while(current!=NULL){
142                 cout<<current->data<<" ";
143                 current=current->link;
144             }
145             cout<<endl;
146         }
147 };
复制代码

测试代码如下:

复制代码
 1 void menu(){
 2     cout<<"1.向前插入建表(-1结束)"<<endl;
 3     cout<<"2.向后插入建表(-1结束)"<<endl;
 4     cout<<"3.输出链表"<<endl;
 5     cout<<"4.搜索元素x所在节点的序号"<<endl;
 6     cout<<"5.取出第i个元素的值"<<endl;
 7     cout<<"6.用x的值修改第i个元素的值"<<endl;
 8     cout<<"7.删除第i个元素"<<endl;
 9     cout<<"8.在第i个元素后面插入x"<<endl;
10     cout<<"9.退出"<<endl;
11 } 
12 
13 template<class T>
14 void function(int num,List<T> *list){
15     switch(num){
16         int x,i;
17         case 1:
18             list->inputFront();
19             break;
20         case 2:
21             list->inputRear();
22             break;
23         case 3:
24             list->output();
25             break;
26         case 4:
27             cin>>x;
28             list->search(x,i);
29             cout<<i<<endl;
30             break;
31         case 5:
32             cin>>i;
33             list->getData(i,x);
34             cout<<x<<endl;
35             break;
36         case 6:
37             cin>>x>>i;
38             list->setData(i,x);
39             break;
40         case 7:
41             cin>>i;
42             list->remove(i,x);
43             break;
44         case 8:
45             cin>>i>>x;
46             list->insert(i,x);
47             break;
48         default:
49             exit(1);
50     }
51 }
52 int main(int argc, char** argv) {
53     int num;
54     List<int> *list=new List<int>(-1);
55     while(true){
56         menu();
57         cin>>num;
58         function(num,list);
59     }
60     return 0; 
61 }
复制代码

 

 

其次是循环链表:

  代码我就不放了,就讲讲要修改的地方:

  1.构造函数。first=new LinkNode<T>;后面接上:first->link=first;目的是让自身首尾相连。如图:

  

   2.在判断循环指针是否到达表尾的条件要从NULL换成first。

   小结:

   循环链表的目的是只要知道表中任一一个节点的地址,就能遍历表中其他任一节点。

最后是双向链表:

  修改:

  1.节点的构造函数。需要增加一个前驱指针。

  2.搜索、插入删除算法。首先是方向,确定方向后算法和单链表差不多,区别在于通过前驱指针还是后续指针访问节点。其次是插入删除,讲起来比较啰嗦,直接看图:

  这是插入(中间是插入元素),虚线是原来的情况,实线是插入后的情况,可见一共涉及到4个指针。

  这是删除(中间是删除元素)。看上去也是涉及4个指针,实际只动了1和4。

  小结:

  双向链表的目的是为了解决在链表中不同方向(前/后)访问元素的问题。

<think>嗯,用户想了解单链表循环链表双向链表的概念及区别。首先,我需要回忆一下数据结构中这些链表的基本定义特点。根据引用[3],链表由一系列节点组成,每个节点包含数据域指针域。单链表每个节点指向下一个节点,而双向链表则有两个指针,分别指向前后节点。循环链表则是尾节点指向头节点,形成一个环。 接下来,要比较它们的区别。比如单链表只能单向遍历,插入删除操作可能需要遍历找到前驱节点,而双向链表因为有前驱后继指针,操作更方便。循环链表在遍历时需要注意避免死循环,但适合环形结构的需求。 然后需要参考引用中的内容来确保准确性。例如引用[5]提到仅有尾指针的单循环链表在尾端插入效率高,不需要遍历整个链表。这可能涉及到不同链表的应用场景。 还要考虑用户可能的深层需求,比如性能比较、适用场景、优缺点等。需要组织回答结构清晰,分点说明每种链表的结构、操作特点、优缺点,最后再对比总结。 可能用户是学习数据结构的学生,需要准备考试或面试,所以应该用简洁明了的语言,结合例子代码片段(如果有的话),但根据用户的要求,这次不需要代码,只要中文解释。 需要确保回答正确,比如单链表删除节点是否需要前驱节点,双向链表如何简化这一过程。循环链表如何实现循环,比如尾节点指向头节点。 最后生成相关问题,可能涉及操作复杂度、应用实例、如何选择链表类型等。要参考示例中的问题,比如性能分析、优化方法、应用场景。</think>### 单链表循环链表双向链表的概念及区别 #### 1. **单链表** - **结构**:每个节点包含一个数据域一个指向下一个节点的指针域。例如: $$ \text{节点} = [\text{数据}| \text{next指针}] $$ - **操作特点**: - 插入/删除节点时,需从头节点遍历至目标位置的前驱节点[^2][^3]。 - 仅支持单向遍历,无法回溯。 - **优缺点**: - 优点:内存占用较少,结构简单。 - 缺点:查找前驱节点效率低,时间复杂度为$O(n)$[^2][^4]。 #### 2. **循环链表** - **结构**:尾节点的指针指向头节点,形成闭环。例如: $$ \text{尾节点} \rightarrow \text{头节点} $$ - **操作特点**: - 插入/删除尾节点时效率高(若使用尾指针),无需遍历整个链表[^5]。 - 遍历需设置终止条件,避免无限循环。 - **应用场景**:轮询任务调度、环形缓冲区。 #### 3. **双向链表** - **结构**:每个节点包含两个指针,分别指向前驱后继节点。例如: $$ \text{节点} = [\text{prev指针}| \text{数据}| \text{next指针}] $$ - **操作特点**: - 可直接通过前驱指针定位前驱节点,插入/删除操作时间复杂度为$O(1)$[^3]。 - 支持双向遍历。 - **优缺点**: - 优点:操作灵活,适用于频繁插入/删除的场景。 - 缺点:内存占用增加(每个节点多一个指针)[^4]。 --- ### **三者对比总结** | 类型 | 遍历方向 | 前驱节点访问 | 内存占用 | 典型应用 | |------------|----------|--------------|----------|------------------------| | 单链表 | 单向 | 需遍历 | 低 | 简单数据序列管理 | | 循环链表 | 单向循环 | 需遍历 | 低 | 环形结构需求(如轮询) | | 双向链表 | 双向 | 直接访问 | 高 | 频繁双向操作的场景 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值