单链表在数据结构中算是一个十分基础的结构。在单链表上可以有很多的其他衍生比如,循环链表,带头结点的单链表,双向链表等等。同时单链表在实际应用中也很有很重要的意义。举例说明:集合的交集、并集问题,使用循环链表可以解决约瑟夫环问题、使用链表可以解决多项式的加法乘法。
也许在以后的学习中很少自己再完整写过链表,但是在数据结构这门课的学习中,将一些基础的链表操作写一遍是很重要的。
单链表的一些优点:相对于顺序表而言,插入删除算法较简便,但是对于查找以及返回某位置的数据却没有顺序表方便。
下面代码给出单链表的结构定义,以及简单的逆序操作。
代码:
1 #include<bits/stdc++.h> 2 3 #define max_size 1000010 4 int a[max_size]; 5 6 using namespace std; 7 8 typedef int DataType;//类型定义 9 typedef struct node //单链表 10 { 11 DataType data; 12 struct node* next; 13 } LinkedNode,*LinkList; 14 15 /****由数组创建单链表****/ 16 LinkList CreateList(DataType a[],int n) 17 { 18 LinkedNode* ListHead=new LinkedNode(); 19 ListHead->data=a[0]; 20 ListHead->next=NULL; 21 for(int i=n-1; i>=1; i--) 22 { 23 LinkedNode* p=new LinkedNode(); 24 p->data=a[i]; 25 p->next=ListHead->next; 26 ListHead->next=p; 27 } 28 return ListHead; 29 } 30 31 void PrintList(LinkList ListHead) 32 { 33 if(NULL==ListHead)cout<<"The List is empty!"<<endl; 34 else 35 { 36 LinkedNode* p=ListHead; 37 while(p!=NULL) 38 { 39 cout<<p->data<<" "; 40 p=p->next; 41 } 42 cout<<endl; 43 } 44 } 45 46 void ReverseList(LinkedNode* pCur,LinkList& ListHead)//递归算法 47 { 48 if( (NULL==pCur)||(NULL==pCur->next) ) 49 { 50 ListHead=pCur; 51 } 52 else 53 { 54 LinkedNode* pNext=pCur->next; 55 ReverseList(pNext,ListHead); //递归逆置后继结点 56 pNext->next=pCur; //将后继结点指向当前结点。 57 pCur->next=NULL; 58 } 59 } 60 61 int main() 62 { 63 64 while(true) 65 { 66 int n; 67 cout<<"请输入创建单链表的长度:"<<endl; 68 cin>>n; 69 for(int i=0; i<n; i++) 70 { 71 cout<<"请输入第"<<i+1<<"个数据:"; 72 cin>>a[i]; 73 } 74 LinkedNode* list=CreateList(a,n); 75 cout<<"逆置前:"<<endl; 76 PrintList(list); 77 LinkedNode*pTemp=list; 78 ReverseList(pTemp,list); 79 cout<<"逆置后:"<<endl; 80 PrintList(list); 81 } 82 return 0; 83 }
下面给出其他操作的代码:
单链表结构定义:
1 //程序2-15(单链表的结构定义) 2 3 typedef int DataType; 4 typedef struct node//链表结点 5 { 6 DataType data;//数据域 7 struct node *link;//链接指针域 8 }LinkNode, *LinkList; 9 10 //单链表适用于插入或删除频繁、存储空间需求不定的情形 11 12 /* (1)单链表中数据的逻辑结构与其物理结构可能不一致, 13 通过指针将各个元素按照线性表的逻辑顺序连接起来 14 (2)单链表的长度可以扩充 15 (3)对单链表的便利和查找只能从头指针指示的首元节点开始, 16 不能像顺序表那样直接访问某个指定的节点 17 (4)当进行插入或删除运算时,只需修改相关结点的指针域即可,既方便又快捷 18 (5)由于单链表的每个结点带有指针域,因而比顺序表需要的存储空间多 19 */
单链表的插入算法:
1 //程序2-16(单链表插入算法) 2 3 int Insert(LinkList& first,int i,DataType x)//将新元素x插入到第i个节点的位置。 4 //i从1开始,i=1,表示插入到原首结点之前 5 { 6 if(!first||i==1)//插入到空表或非空表的首元结点 7 { 8 LinkNode *newnode=new LinkNode;//建立新的结点 9 if(!newnode){cerr<<"存储分配错误! ";exit(1);} 10 newnode->data=x; 11 newnode->link=first;first=newnode;//新结点成为第一个结点 12 } 13 else//插入到链中或链尾 14 { 15 LinkNode *p=first;//为了找到第i-1个结点,需要新建一个指针来找到第i-1个结点 16 int k=1; 17 while(P!=NULL&&k<i-1) 18 { 19 p=p->link; 20 k++; 21 }//利用循环将p指向第i-1个结点的位置 22 if(p==NULL&&first) 23 {cerr<<"无效的插入位置! ";return 0;}//判断是否合理,如果表为空或者表太短则返回0 24 else//接下来就开始插入 25 { 26 LinkNode *newnode=new LinkNode;//建立一个新结点 27 if(!newNode){cerr<<"存储分配错误! ";exit(1);} 28 newnode->data=x; 29 newnode->link=p->link; 30 p->link=newnode; 31 } 32 } 33 return 1; 34 };
单链表的删除算法:
1 //程序2-17(单链表的删除算法) 2 3 int Remove(LinkList &first,int i,DataType &x)//将单链表的第i个元素删去,i从1开始, 4 // 引用型参数x返回被删元素的值 5 { 6 LinkNode *q,*p;//p来表示第i-1个结点的位置,q来表示要删除的结点 7 int k; 8 if(i<=1){q=first,first=first->link;}//删除首结点 9 else//删除链表中部或尾部结点 10 { 11 p=first; 12 k=1; 13 while(p!=NULL&&k<i-1)//找到第i-1个节点的位置 14 { 15 p=p->link; 16 k++; 17 } 18 if(p==NULL||p->link==NULL)//如果表太短或表为空 则返回0 19 {cerr<<"无效的删除位置! ";return 0;} 20 q=p->link;//保存第i个结点 21 p->link=q->link;//将第i-1个结点和第i+1个结点链接 22 } 23 x=q->data;//取出被删结点的数据值 24 delete q;//删除结点 25 return 1 26 }
单链表的一些其他操作(初始化、表空、清空、表长计算等):
1 //程序2-18(单链表部分常用操作的实现) 2 //带头结点的单链表 3 void initList(LinkList &first)//初始化单链表 4 { 5 first=new LinkNode; 6 if(!first){cerr<<"存储分配错误! ";exit(1);} 7 first->link=NULL; 8 }; 9 10 void clearList(LinkList &first)//清空单链表 11 { 12 LinkNode *q;//借用指针 13 while(first->link!=NULL)//当链表不空时,删除所有的结点 14 { 15 q=first->link; 16 first->link=q->link;//保存被删结点,从链表上摘下该结点 17 delete q;//删除该结点 18 } 19 }; 20 21 int Length(LinkList &first)//计算表的长度(结点个数减一) 22 { 23 LinkNode *p=first->link; 24 int count=0; 25 while(p!=NULL) 26 { 27 p=p->link; 28 count++; 29 } 30 }; 31 32 int isEmpty(LinkList &first)//判断单链表是否为空,为空则返回1;不空则返回0 33 { 34 return (first->link==NULL); 35 } 36 37 LinkLode* Search(LinkList &first,DataType x)//在单链表中查找第一个等于x的结点,查找成功则返回该结点的位置,否则返回NULL 38 { 39 LinkNode *p=first->link; 40 while(p!=NULL&&p->data!=x) 41 { 42 p=p->link; 43 } 44 return p; 45 }; 46 47 LinkNode *Locate(LinkList &first,int i)//在单链表对第i个结点定位,函数返回第i个结点的位置,否则返回NULL 48 { 49 if(i<0)return NULL; 50 LinkNode *p=first; 51 int k=0; 52 while(P!=NULL&&k<i) 53 { 54 p=p->link; 55 k++; 56 } 57 return p; 58 }; 59 60 int Insert(LinkList &first,int i,DataType x)//将新的元素x插入到表中第i个位置,如果i不合理则函数返回0,否则返回1 61 { 62 LinkNode *p=Locate(first,i-1);//调用定位函数,将p指针指向单链表的第i个位置 63 if(p==NULL)return 0;//如果i不合理,返回0 64 LinkNode *s=new LinkNode; 65 if(!s){cerr<<"存储分配错误! ";exit(1);} 66 s->data=x; 67 s->link=p->link;//将s链接到p之后 68 p->link=s;//调整p的下一个结点,指向s 69 return 1;//插入成功,函数返回1 70 }; 71 72 int Remove(LinkList &first,int i,DataType &x) 73 { 74 LinkNode *p=Locate(first,i-1);//将p指向单链表的第i-1个位置 75 if(P==NULL||p->link==NULL)return 0;//i不合理则函数返回0 76 LinkNode *q=p->link;//用q来保存被删的结点 77 p->link=q->link;//重新连接 将第i-1个结点和第i+1个结点链接 78 x=q->data;//取出被删结点的数据 79 delete q;//删除这个结点 80 return 1;//删除成功则函数返回1 81 }; 82 83 void Copy(LinkList &first1,LinkList &first2) 84 //函数假定链表first1已存在且为空,即first1的头结点已创建且first1->link==NULL; 85 //复制链表first2的全部结点到空链表first1中 86 { 87 LinkNode *srcptr=frist2->link;//srcptr是源指针 88 LinkNode *destptr=first1;//destptr是目标链尾指针 89 while(srcptr->link!=NULL)//利用while循环,将first2中的每个结点复制到first1中 90 { 91 destptr->link=new LinkNode;//新结点链接到目标链尾 92 if(!destptr){cerr<<"存储分配错误! ";exit(1);} 93 destptr=destptr->link; 94 destptr->data=srcptr->data;//节点数据的传送 95 srcptr=srcptr->link;//传送源链指针到下一结点 96 } 97 destptr->link=NULL;//目标链收尾 98 };