线性表
以c为主要的语言描述,但是引入了c++中“传引用”的写法,以及其他相对便捷的操作
我已对所有的函数接口进行了调试,保证可正常调用运行,但可能个别没有考虑到特殊的边界所产生的bug希望各位指正
线性表的ADT
线性表是最常见且最简单的一种数据结构。一个线性表的n个元素的有限序列。以下是线性表的ADT描述,再之后的代码实现中,仅对部分函数接口给予实现(可能会适当的对函数功能进行修改)。
| //线性表的ADT描述 |
| ADT List{ |
| 数据对象:D={ai|ai ∈ Elemset , i=1,2,3...n,n>=0} |
| 数据关系:R1={<ai-1,ai>|ai-1,ai∈D,i=2,3...n} |
| 基本操作: |
| InitList(&L) |
| 操作结果:构造一个空的线性表。 |
| DestroyList(&L) |
| 初始条件:线性表L已存在。 |
| 操作结果:销毁线性表L。 |
| ClearList(&L) |
| 初始条件:线性表L已存在。 |
| 操作结果:将L重置为空表。 |
| ListEmpty(L) |
| 初始条件:线性表L已存在。 |
| 操作结果:若L为空表,返回True,否则返回false。 |
| ListLength(L) |
| 初始条件:线性表L已存在。 |
| 操作结果:返回L中数据元素的个数。 |
| GetElem(L,i,&e) |
| 初始条件:线性表L已存在,0<=i<ListLength(L)。 |
| 操作结果:用e返回L中下标为i数据元素的值。 |
| LocateElem(L,e,compare()) |
| 初始条件:线性表L已存在,compare()是数据元素判定的比较函数。 |
| 操作结果:返回L中第一个与e满足compare的数据元素的为序。若不存在,则返回-1。 |
| ListInsert(&L,i,e) |
| 初始条件:线性表L已存在,1<=i<=ListLength(L)+1。 |
| 操作结果:在L中的第i个位置之前插入新的数据元素e,L的长度加1。 |
| ListDelete(&L,i,&e) |
| 初始条件:线性表L已存在,1<=i<=ListLength(L)。 |
| 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1。 |
| ListTraverse(L,visit()) |
| 初始条件:线性表L已存在。 |
| 操作结果:依次对L的每个数据元素调用visit()。如果visit失败,则操作失败。 |
| } |
顺序表
顺序表使用一块连续的空间存放若干数量的元素,可以随机存取,但增、删等修改性能较差。
顺序表的定义
| |
| #define LIST_INIT_SIZE 100 |
| typedef int Elem; |
| typedef struct list{ |
| Elem *p; |
| unsigned int len; |
| }SqList; |
顺序表函数接口的实现
| |
| #include<stdio.h> |
| #include<stdlib.h> |
| |
| |
| void InitList(SqList &L){ |
| L.p=(Elem*)malloc(sizeof(Elem)*LIST_INIT_SIZE); |
| L.len=0; |
| if(L.p==NULL){ |
| printf("List init failed. Error: Memory allocation failed.\n"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| |
| void DestroyList(SqList &L){ |
| free(L.p); |
| } |
| |
| |
| void ClearList(SqList &L){ |
| L.len=0; |
| } |
| |
| |
| bool ListEmpty(SqList L){ |
| return !L.len; |
| } |
| |
| |
| unsigned int ListLength(SqList L){ |
| return L.len; |
| } |
| |
| |
| bool GetElem(const SqList L,unsigned int i,Elem &e){ |
| if(i>=L.len) return false; |
| e=L.p[i]; |
| return true; |
| } |
| |
| |
| bool ListInsert(SqList &L,unsigned int i,Elem e){ |
| |
| if(i>L.len||L.len==LIST_INIT_SIZE) return false; |
| for(int j=L.len;j>i;j--) |
| L.p[j]=L.p[j-1]; |
| L.p[i]=e; L.len++; |
| return true; |
| } |
| |
| |
| bool ListDelete(SqList &L,unsigned int i,Elem &e){ |
| if(i>=L.len||L.len==0) return false; |
| e=L.p[i]; |
| for(int j=i;j<L.len;j++) |
| L.p[j]=L.p[j+1]; |
| L.len--; |
| return true; |
| } |
| |
| |
| void ListTraverse(SqList L){ |
| for(int i=0;i<L.len;i++){ |
| if(i) putchar(' '); |
| printf("%d",L.p[i]); |
| } |
| } |
常见顺序表的算法实现
将两个递增的顺序表合并
| |
| |
| SqList MergeList(SqList a,SqList b){ |
| SqList ret; |
| int len_a=a.len,len_b=b.len; |
| int i=0,j=0,index=0; |
| ret.len=len_a+len_b; |
| ret.p=(Elem*)malloc(sizeof(Elem)*ret.len); |
| if(ret.p==NULL) exit(EXIT_FAILURE); |
| while(i<len_a&&j<len_b){ |
| if(a.p[i]<b.p[j]) ret.p[index++]=a.p[i++]; |
| else ret.p[index++]=b.p[j++]; |
| } |
| |
| while(i<len_a) ret.p[index++]=a.p[i++]; |
| while(j<len_b) ret.p[index++]=b.p[j++]; |
| return ret; |
| } |
删除顺序表中所有值为x的元素
| |
| |
| int Delete_x(SqList &L,Elem x){ |
| int cnt=0,ret; |
| for(int i=0;i<L.len;i++){ |
| if(L.p[i]!=x) |
| L.p[cnt++]=L.p[i]; |
| } |
| ret=L.len-cnt; |
| L.len=cnt; |
| return ret; |
| } |
删除顺序表中值属于[s,t]的元素
| |
| void Delete_st(SqList &L,Elem s,Elem t){ |
| int index=0; |
| for(int i=0;i<L.len;i++){ |
| if(L.p[i]<s||L.p[i]>t) |
| L.p[index++]=L.p[i]; |
| } |
| L.len=index; |
| } |
统计顺序表中的主元(出现次数超过一半的称为主元)
在此假定前提条件顺序表不为空
| |
| |
| Elem Majority(SqList L){ |
| int cnt=1; |
| Elem ret=L.p[0]; |
| for(int i=1;i<L.len;i++){ |
| if(L.p[i]==ret) cnt++; |
| else{ |
| if(cnt>0) cnt--; |
| else{ |
| ret=L.p[i];cnt=1; |
| } |
| } |
| } |
| return ret; |
| } |
将线性表L在[i,j]区间内的元素逆置
| |
| void Reverse(SqList &L,int i,int j){ |
| while(i<j){ |
| Elem temp=L.p[i]; |
| L.p[i]=L.p[j]; |
| L.p[j]=temp; |
| i++;j--; |
| } |
| } |
将线性表循环左移k个元素(借助逆置函数)
| |
| void Rotate_left(SqList &L,int k){ |
| Reverse(L,0,k-1); |
| Reverse(L,k,L.len-1); |
| Reverse(L,0,L.len-1); |
| } |
测试顺序表的main函数
| int main(void){ |
| |
| SqList L1,L2; |
| InitList(L1);InitList(L2); |
| printf("已经初始化L1和L2...\n"); |
| |
| printf("L1是否为空:"); |
| if(ListEmpty(L1)) printf("为空\n"); |
| else printf("非空\n"); |
| |
| Elem temp; |
| for(int i=20;i>=0;i--){ |
| temp=i; |
| ListInsert(L1,0,temp); |
| } |
| for(int i=25;i>=5;i--){ |
| temp=i; |
| ListInsert(L2,0,temp); |
| } |
| |
| printf("顺序表L1内容如下:\n"); |
| ListTraverse(L1); putchar('\n'); |
| printf("顺序表L2内容如下:\n"); |
| ListTraverse(L2); putchar('\n'); |
| |
| SqList L3= MergeList(L1,L2); |
| printf("顺序表合并如下:\n"); |
| ListTraverse(L3); putchar('\n'); |
| |
| if(GetElem(L3,5,temp)){ |
| printf("查找下标为5的元素成功,值为:%d\n",temp); |
| printf("顺序表L3内容如下:\n"); |
| ListTraverse(L3); putchar('\n'); |
| } |
| printf("删除下标为10的元素:\n"); |
| if(ListDelete(L3,10,temp)){ |
| printf("删除下标为10的元素成功,值为:%d\n",temp); |
| printf("顺序表L3删除下标为10的元素后内容如下:\n"); |
| ListTraverse(L3); putchar('\n'); |
| } |
| printf("删除值属于[11,15]的元素:\n") ; |
| Delete_st(L3,11,15); |
| printf("顺序表L3删除11-15之间的元素后内容如下:\n"); |
| ListTraverse(L3); putchar('\n'); |
| |
| |
| printf("对L3循环左移6个元素测试:\n"); |
| Rotate_left(L3,6); |
| printf("顺序表L3循环左移6个元素后内容如下:\n"); |
| ListTraverse(L3); putchar('\n'); |
| |
| return 0; |
| } |
输出结果如下:
| 已经初始化L1和L2... |
| L1是否为空:为空 |
| 顺序表L1内容如下: |
| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
| 顺序表L2内容如下: |
| 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
| 顺序表合并如下: |
| 0 1 2 3 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 20 20 21 22 23 24 25 |
| 查找下标为5的元素成功,值为:5 |
| 顺序表L3内容如下: |
| 0 1 2 3 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 20 20 21 22 23 24 25 |
| 删除下标为10的元素: |
| 删除下标为10的元素成功,值为:7 |
| 顺序表L3删除下标为10的元素后内容如下: |
| 0 1 2 3 4 5 5 6 6 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 20 20 21 22 23 24 25 |
| 删除值属于[11,15]的元素: |
| 顺序表L3删除11-15之间的元素后内容如下: |
| 0 1 2 3 4 5 5 6 6 7 8 8 9 9 10 10 16 16 17 17 18 18 19 19 20 20 21 22 23 24 25 |
| 对L3循环左移6个元素测试: |
| 顺序表L3循环左移6个元素后内容如下: |
| 5 6 6 7 8 8 9 9 10 10 16 16 17 17 18 18 19 19 20 20 21 22 23 24 25 0 1 2 3 4 5 |
链表
单链表
单链表的定义
链表使用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是练习的,也可以是不连续的)。数据元素以结点的形式存在,一个结点包含两个域:存储数据元素信息的域称为数据域;存储直接后继存储位置的域成为指针域;结点之间通过指针域连接形成单链表。
| typedef int Elem; |
| typedef struct information{ |
| int tag; |
| }Inform; |
| typedef struct Node{ |
| Elem data; |
| struct Node *next; |
| Inform info; |
| }LNode,*LinkList; |
| |
| |
| void PrintData(Elem e){ |
| printf("%d",e); |
| } |
单链表函数接口的实现
不带头结点的写法:
不带头结点的情况下,链表指针指向第一个结点(空时为NULL),此使链表可能因为增删等原因导致链表指针发生改变。
| |
| void InitList(LinkList &L){ |
| L=NULL; |
| } |
| |
| |
| void DestoryList(LinkList &L){ |
| while(L){ |
| LNode *temp=L; |
| L=L->next; |
| free(temp); |
| } |
| } |
| |
| |
| bool IsEmpty(LinkList L){ |
| return !L; |
| } |
| |
| |
| bool IsFull(LinkList L){ |
| LNode *temp=(LNode *)malloc(sizeof(LNode)); |
| if(temp==NULL) return true; |
| else{ |
| free(temp); |
| return false; |
| } |
| } |
| |
| |
| int ListLength(LinkList L){ |
| LNode *p=L; |
| int cnt=0; |
| while(p){ |
| p=p->next; |
| cnt++; |
| } |
| return cnt; |
| } |
| |
| |
| bool GetElem(LinkList L,int i,Elem &e){ |
| if(i<=0||i>ListLength(L)){ |
| printf("Error: index out of range.\n"); |
| return false; |
| } |
| LNode *p=L; |
| while(--i) |
| p=p->next; |
| e=p->data; |
| return true; |
| } |
| |
| |
| int LocateElem(LinkList L,Elem x){ |
| int index=0; |
| while(L){ |
| index++; |
| if(L->data==x) return index; |
| L=L->next; |
| } |
| return -1; |
| } |
| |
| |
| bool HeadInsert(LinkList &L,Elem x){ |
| LNode *temp=(LNode *)malloc(sizeof(LNode)); |
| if(temp==NULL) return false; |
| temp->data=x; |
| temp->next=L; |
| L=temp; |
| return true; |
| } |
| |
| |
| bool TailInsert(LinkList &L,Elem x){ |
| LNode *tail=L; |
| LNode *temp=(LNode *)malloc(sizeof(LNode)); |
| if(temp==NULL) return false; |
| temp->data=x;temp->next=NULL; |
| if(tail==NULL){ |
| L=temp; |
| } |
| else{ |
| while(tail->next) |
| tail=tail->next; |
| tail->next=temp; |
| } |
| return true; |
| } |
| |
| |
| bool InsertList_i(LinkList &L,unsigned int i,Elem x){ |
| int len=ListLength(L); |
| LNode *temp=(LNode*)malloc(sizeof(LNode)); |
| LNode *p=L; |
| if(len==0||i>len+1||i<=0||temp==NULL) |
| return false; |
| temp->data=x; |
| if(i==1){ |
| temp->next=L; |
| L=temp; |
| } |
| else{ |
| i--; |
| while(--i) |
| p=p->next; |
| temp->next=p->next; |
| p->next=temp; |
| } |
| return true; |
| } |
| |
| |
| bool DeleteList_i(LinkList &L,unsigned int i,Elem &e){ |
| int len=ListLength(L); |
| if(len==0||i>len||i<=0) |
| return false; |
| LNode* p=L; |
| if(i==1){ |
| e=L->data; |
| L=L->next; |
| free(p); |
| } |
| else{ |
| i--; |
| while(--i) |
| p=p->next; |
| LNode *temp=p->next; |
| e=temp->data; |
| p->next=temp->next; |
| free(temp); |
| } |
| return true; |
| } |
| |
| |
| void TraverseList(LinkList L){ |
| int cnt=0; |
| while(L){ |
| if(cnt++) printf("->"); |
| PrintData(L->data); |
| L=L->next; |
| } |
| } |
带头结点的写法:
带头结点的情况下,链表指针指向一个空的结点,该空结点数据域不保存数据元素,指针域指向链表的第一个结点(初始为NULL)。通常来说带头结点的链表各种操作实现容易,且易于理解,因此之后的常见算法按照带头结点的链表给予实现。
| |
| bool InitList(LinkList &L){ |
| L=(LNode*)malloc(sizeof(LNode)); |
| if(L==NULL) return false; |
| L->next=NULL; |
| return true; |
| } |
| |
| |
| void DestoryList(LinkList &L){ |
| while(L){ |
| LNode *temp=L; |
| L=L->next; |
| free(temp); |
| } |
| } |
| |
| |
| bool IsEmpty(LinkList L){ |
| return !L->next; |
| } |
| |
| |
| bool IsFull(LinkList L){ |
| LNode *temp=(LNode *)malloc(sizeof(LNode)); |
| if(temp==NULL) return true; |
| else{ |
| free(temp); |
| return false; |
| } |
| } |
| |
| |
| int ListLength(LinkList L){ |
| LNode *p=L->next; |
| int cnt=0; |
| while(p){ |
| p=p->next; |
| cnt++; |
| } |
| return cnt; |
| } |
| |
| |
| bool GetElem(LinkList L,int i,Elem &e){ |
| if(i<=0||i>ListLength(L)){ |
| printf("Error: index out of range.\n"); |
| return false; |
| } |
| LNode *p=L->next; |
| while(--i) |
| p=p->next; |
| e=p->data; |
| return true; |
| } |
| |
| |
| int LocateElem(LinkList L,Elem x){ |
| int index=0; |
| L=L->next; |
| while(L){ |
| index++; |
| if(L->data==x) return index; |
| L=L->next; |
| } |
| return -1; |
| } |
| |
| |
| bool HeadInsert(LinkList &L,Elem x){ |
| LNode *temp=(LNode *)malloc(sizeof(LNode)); |
| if(temp==NULL) return false; |
| temp->data=x; |
| temp->next=L->next; |
| L->next=temp; |
| return true; |
| } |
| |
| |
| bool TailInsert(LinkList &L,Elem x){ |
| LNode *tail=L->next; |
| LNode *temp=(LNode *)malloc(sizeof(LNode)); |
| if(temp==NULL) return false; |
| temp->data=x;temp->next=NULL; |
| if(tail==NULL){ |
| L->next=temp; |
| } |
| else{ |
| while(tail->next) |
| tail=tail->next; |
| tail->next=temp; |
| } |
| return true; |
| } |
| |
| |
| bool InsertList_i(LinkList L,unsigned int i,Elem x){ |
| int len=ListLength(L); |
| LNode *temp=(LNode*)malloc(sizeof(LNode)); |
| LNode *p=L; |
| if(i>len+1||i<=0||temp==NULL) |
| return false; |
| temp->data=x; |
| while(--i) |
| p=p->next; |
| temp->next=p->next; |
| p->next=temp; |
| return true; |
| } |
| |
| |
| bool DeleteList_i(LinkList L,unsigned int i,Elem &e){ |
| int len=ListLength(L); |
| if(len==0||i>len||i<=0) |
| return false; |
| LNode* p=L; |
| while(--i) |
| p=p->next; |
| LNode *temp=p->next; |
| e=temp->data; |
| p->next=temp->next; |
| free(temp); |
| return true; |
| } |
| |
| |
| void TraverseList(LinkList L){ |
| int cnt=0; |
| L=L->next; |
| while(L){ |
| if(cnt++) printf("->"); |
| PrintData(L->data); |
| L=L->next; |
| } |
| } |
常见链表算法的实现
此处的算法的实现均采用带头结点的单链表进行实现。
| |
| void Delete_x(LinkList &L,Elem x){ |
| LNode *pre=L,*p=L->next,*temp; |
| while(p!=NULL){ |
| if(p->data==x){ |
| temp=p; |
| p=p->next; |
| pre->next=p; |
| free(temp); |
| } |
| else{ |
| pre=p; p=p->next; |
| } |
| } |
| } |
| |
| |
| void Delete_st(LinkList &L,Elem s,Elem t){ |
| if(s>t){ |
| Elem tmp=t; t=s; s=tmp; |
| } |
| LNode *pre=L,*p=L->next,*temp; |
| while(p!=NULL){ |
| if(p->data>=s&&p->data<=t){ |
| temp=p; |
| p=p->next; |
| pre->next=p; |
| free(temp); |
| } |
| else{ |
| pre=p; p=p->next; |
| } |
| } |
| } |
| |
| |
| void R_TraverseList(LinkList L){ |
| static int count=0; |
| L=L->next; |
| if(L->next!=NULL) |
| R_TraverseList(L); |
| if(count++) printf("->"); |
| PrintData(L->data); |
| } |
| |
| |
| void Reverse(LinkList L){ |
| LNode *p,*r; |
| p=L->next; |
| L->next=NULL; |
| while(p!=NULL){ |
| r=p->next; |
| p->next=L->next; |
| L->next=p; |
| p=r; |
| } |
| } |
| |
| |
| void K_Reverse(LinkList L,const unsigned int k){ |
| int cnt=0,len=ListLength(L); |
| if(len==0||k==0||len<k) return; |
| LNode *p=L->next; |
| LNode *head=L; |
| LNode *rear=L->next; |
| L->next=NULL; |
| int dealnum=len-len%k; |
| while(cnt!=dealnum){ |
| LNode *temp=p->next; |
| p->next=head->next; |
| head->next=p; |
| p=temp; |
| cnt++; |
| if(cnt%k==0){ |
| head=rear; |
| rear=temp; |
| } |
| } |
| if(cnt!=len) |
| head->next=rear; |
| } |
| |
| |
| void R_Search(LinkList L,const unsigned int k){ |
| if(k==0){ |
| printf("Not Found"); return; |
| } |
| LNode *p=L->next,*q=L->next; |
| int count=0; |
| while(p!=NULL){ |
| if(count<k) count++; |
| else q=q->next; |
| p=p->next; |
| } |
| if(count<k) printf("Not Found\n"); |
| else PrintData(q->data); |
| } |
测试单链表的main函数
用于测试的main函数都调用了带头结点的函数和接口实现,因此使用时的函数声明请统一使用带头结点的写法。
需要使用std命名空间,并且包含string头文件,以便于main函数内打印分割线。
| int main(void){ |
| |
| string str="--------------------------------------------------------------------------"; |
| LinkList L; |
| InitList(L); |
| cout<<str<<endl; |
| printf("初始化后的链表长度为 %d 。\n",ListLength(L)); |
| Elem temp,temp1,temp2; |
| for(int i=0;i<20;i++){ |
| temp=i; |
| if(i<10) HeadInsert(L,temp); |
| else TailInsert(L,temp); |
| } |
| |
| printf("链表长度为 %d ,其结构如下:\n",ListLength(L)); |
| TraverseList(L); |
| |
| cout<<str<<endl; |
| printf("读取链表的第10个元素(从1开始计算),并用temp接收:"); |
| GetElem(L,10,temp); |
| PrintData(temp); putchar('\n'); |
| |
| cout<<str<<endl; |
| temp=13; |
| printf("链表中结点数据等于temp(其中值为13)的序号为: %d\n",LocateElem(L,temp)); |
| temp=99; |
| printf("链表中没有结点数据等于temp(其中值为99)的,因此打印: %d\n",LocateElem(L,temp)); |
| |
| cout<<str<<endl; |
| temp=66; |
| printf("在链表中第3个位置插入一个结点数值为66的结点,插入后链表结构为:\n"); |
| InsertList_i(L,3,temp); |
| TraverseList(L); |
| |
| cout<<str<<endl; |
| printf("删除链表中第4个结点(从1开始计算),并用temp返回其值:"); |
| DeleteList_i(L,4,temp); |
| PrintData(temp); putchar('\n'); |
| printf("删除后的链表结构如下:\n"); |
| TraverseList(L); |
| |
| cout<<str<<endl; |
| temp1=10,temp2=14; |
| Delete_st(L,temp1,temp2); |
| printf("删除链表中值位于10~14的结点,删除后的链表结构如下:\n"); |
| TraverseList(L); |
| |
| cout<<str<<endl; |
| printf("逆序输出链表,结果如下:\n"); |
| R_TraverseList(L); putchar('\n'); |
| |
| cout<<str<<endl; |
| printf("链表整体逆置,逆置之后的链表结构如下:\n"); |
| TraverseList(L); |
| |
| cout<<str<<endl; |
| printf("链表每K个逆置,结尾处不足k个的不用逆置,每4个逆置结果如下:\n"); |
| K_Reverse(L,4); |
| TraverseList(L); |
| |
| cout<<str<<endl; |
| printf("输出倒数第k个结点元素,这里k取值为4,结果如下:"); |
| R_Search(L,4); putchar('\n'); |
| |
| cout<<str<<endl; |
| return 0; |
| } |
输出结果如下:
| -------------------------------------------------------------------------- |
| 初始化后的链表长度为 0 。 |
| 链表长度为 20 ,其结构如下: |
| 9->8->7->6->5->4->3->2->1->0->10->11->12->13->14->15->16->17->18->19 |
| -------------------------------------------------------------------------- |
| 读取链表的第10个元素(从1开始计算),并用temp接收:0 |
| -------------------------------------------------------------------------- |
| 链表中结点数据等于temp(其中值为13)的序号为: 14 |
| 链表中没有结点数据等于temp(其中值为99)的,因此打印: -1 |
| -------------------------------------------------------------------------- |
| 在链表中第3个位置插入一个结点数值为66的结点,插入后链表结构为: |
| 9->8->66->7->6->5->4->3->2->1->0->10->11->12->13->14->15->16->17->18->19 |
| -------------------------------------------------------------------------- |
| 删除链表中第4个结点(从1开始计算),并用temp返回其值:7 |
| 删除后的链表结构如下: |
| 9->8->66->6->5->4->3->2->1->0->10->11->12->13->14->15->16->17->18->19 |
| -------------------------------------------------------------------------- |
| 删除链表中值位于10~14的结点,删除后的链表结构如下: |
| 9->8->66->6->5->4->3->2->1->0->15->16->17->18->19 |
| -------------------------------------------------------------------------- |
| 逆序输出链表,结果如下: |
| 19->18->17->16->15->0->1->2->3->4->5->6->66->8->9 |
| -------------------------------------------------------------------------- |
| 链表整体逆置,逆置之后的链表结构如下: |
| 9->8->66->6->5->4->3->2->1->0->15->16->17->18->19 |
| -------------------------------------------------------------------------- |
| 链表每K个逆置,结尾处不足k个的不用逆置,每4个逆置结果如下: |
| 6->66->8->9->2->3->4->5->16->15->0->1->17->18->19 |
| -------------------------------------------------------------------------- |
| 输出倒数第k个结点元素,这里k取值为4,结果如下:1 |
| -------------------------------------------------------------------------- |
单链表算法补充(伪代码)
1.拆分问题
设带头结点的单链表A={a1,b1,a2,b2,a3,b3...an,bn},将其拆分为带头结点的链表A={a1,a2,a3,a4,a5...an}和B={bn,bn-1,bn-2...b1},其中B作为返回值返回带头结点的单链表头指针。
| |
| |
| |
| LinkList Dis_creat(LinkList A){ |
| LinkList B=(LinkList)malloc(sizeof(LNode)); |
| B->next=NULL; |
| LinkList p=A->next; |
| LinkList q,ra=A; |
| while(p!=NULL){ |
| ra->next=p;ra=p; |
| p=p->next; |
| q=p->next; |
| p->next=B->next; |
| B->next=p; |
| p=q; |
| } |
| ra->next=NULL; |
| return B; |
| } |
2.求两个链表的并
两个递增链表,合并为一个递减单链表,要求使用原来的链表节点存放数据元素。
| |
| |
| |
| |
| void MergeList(LinkList La,LinkList Lb){ |
| LinkList q,pa=La->next;pb=Lb->next; |
| La->next=NULL; |
| while(pa&&pb){ |
| if(pa->data<pb->data){ |
| q=pa->next; |
| pa->next=La->next; |
| La->next=pa; |
| pa=q; |
| } |
| else{ |
| q=pb->next; |
| pb->next=La->next; |
| La->next=pb; |
| pb=q; |
| } |
| } |
| if(pb) pa=pb; |
| while(pa){ |
| q=pa->next; |
| pa->next=La->next; |
| La->next=pa; |
| pa=q; |
| } |
| free(Lb); |
| } |
3.求两个链表的交
A、B两个带头结点的单链表,元素递增有序,A和B中产生C来存放共同元素(不能破坏A、B中的节点)。
| |
| |
| |
| |
| |
| LinkList Get_common(LinkList A,LinkList B){ |
| LinkList C=(LinkList)malloc(sizeof(LNode)); |
| LinkList r=C,pa,pb,tmp; |
| while(pa&&pb){ |
| if(pa->data!=pb->data){ |
| if(pa->data<pb->data) |
| pa=pa->next; |
| else |
| pb=pb->next; |
| } |
| else{ |
| tmp=(LinkList)malloc(sizeof(LNode)); |
| tmp->data=pa->data; |
| r=>next=tmp; |
| r=tmp; |
| pa=pa->next; pb=pb->next; |
| } |
| } |
| r->next=NULL; |
| return C; |
| } |
4.字符序列匹配
两个使用链表存储的字符序列A和B,判定B是否为A中的子序列。(两个链表均无头指针)
| |
| |
| |
| bool Pattern(LinkList A,LinkList B){ |
| LinkList p=A,q=B; |
| LinkList pre=p; |
| while(p&&q){ |
| if(p->data==q->data){ |
| p=p->next; |
| q=q->next; |
| } |
| else{ |
| pre=pre->next; |
| p=pre; |
| q=B; |
| } |
| } |
| if(q==NULL) return true; |
| else return false; |
| } |
5.寻找两个链表的公共结点
现有两个链表,如果有相同的后缀,则公共后缀部分共享相同的链表结点。请设计一个算法找出两个链表的公共结点,如果存在则返回公共结点的指针,否则返回NULL。(两个链表无头结点)
| |
| |
| |
| |
| |
| int Getlength(LinkList L){ |
| int len=0; |
| while(L->next!=NULL){ |
| len++; |
| L=L->next; |
| } |
| return len; |
| } |
| LinkList Search_first_commen(LinkList L1,LinkList L2){ |
| int len1=Getlength(L1),len2=Getlength(L2); |
| int dist=len2-len1; |
| if(Len1<len2){ |
| LinkList tmp=L1; |
| L1=L2; |
| L2=tmp; |
| dist=-dist; |
| } |
| while(dist--) L1=L1->next; |
| while(L1->next!=NULL&&L1->next!=L2->next){ |
| L1=L1->next; L2=L2->next; |
| } |
| return L1->next; |
| } |
| |
| |
| |
| |
| |
| |
| LinkList search_first_common(LinkList L1,LinkList L2){ |
| int ret=NULL; |
| while(L1){ |
| L1->info=1; |
| L1=L1->next; |
| } |
| while(L2){ |
| if(L2->info==1){ |
| ret=L2;break; |
| } |
| L2=L2->next; |
| } |
| return ret; |
| } |
循环链表
循环链表时另一种形式的链式存储结构。其特点是表中最后一个结点的指针域指向头结点,使整个链表形成一个环,从任意一个结点可以到达表中其他的结点。
循环链表的操作和线性链表基本一致,主要差别时在循环条件的判别上,对于循环链表来说:
| //假设对于带头结点的循环链表L,工作指针为p |
| //判断链表为空 |
| if(L->next==L) return true; //如果为空 |
| else return false; //如果不空 |
| //判断链表是否到了结尾的判断条件 |
| p->next==L; //如果工作指针的下一个位置到了表头结点则遍历结束 |
循环链表的算法(伪代码)
1.两个循环链表的合并
已知两个具有头结点的循环链表,链表的头结点的指针分别为h1和h2,写一个函数将h2链接到h1之后,链接后保持链表的循环形式。
| |
| |
| void MergeCLink(LinkList h1,LinkList h2){ |
| LinkList p=h1,q=h2; |
| while(p->next!=h1) p=p->next; |
| while(q->next!=h2) q=q->next; |
| p->next=h2->next; free(h2); |
| q->next=h1; |
| } |
双向链表
非循环双向链表
双向循环链表的的指针包含一个指向前驱的指针和一个指向后继的指针,由此可以分别向前或者向后进行遍历。
非循环双向链表的算法(伪代码)
1.用带头结点的非循环双链表建立一个频度有限的排序函数
设头指针为L的带头结点的非循环双链表,每个节点有pre(前驱指针)、data(数据域)、next(后继指针)、freq(访问频度域),其中访问频度域在链表启用前初始化为0,当Locate(L,x)函数运算一次,则令值为x的的节点中freq的值增加1,并使得链表以freq非增的顺序排序,最近访问的节点排在频度相同的结点前面,以便频繁访问的结点总靠近表头,编写Locate(L,x),返回找到的结点地址,类型为指针类型。
结点类型描述。
| typedef struct DNode{ |
| Elem data; |
| struct DNode *pre,*next; |
| int freq; |
| }DNode,*DLinkList; |
算法:
| |
| |
| |
| |
| DLinkList Locate(DLinkList L,Elem x){ |
| DNode *p=L->next; |
| DNode *q; |
| while(p&&p->data!=x) p=p->next; |
| if(p==NULL){ |
| printf("Not Find!"); |
| } |
| else{ |
| p->freq++; |
| p->next->pre=p->next; |
| p->pre->next=p->next; |
| q=p->pre; |
| while(q!=L&&q->freq<=p->freq) q=q->pre; |
| p->next=q->next; |
| q->next->pre=p; |
| p->pre=q; |
| q->next=p; |
| } |
| return p; |
| } |
双向循环链表的定义和实现(带头结点)
双向循环链表的的指针包含一个指向前驱的指针和一个指向后继的指针,并且头节点的前驱为尾指针,尾指针的后继为头结点。因此,只需对单链表的结点定义做适当的修改即可,Elem的定义不需要改动:
| typedef struct DuNode{ |
| Elem data; |
| struct DuNode *pre; |
| struct DuNode *next; |
| Inform info; |
| }DULNode,*DULinkList; |
其中,仅仅涉及单向操作的算法实现方法与单链表类似,只有插入和删除时的勾链操作有所不同,因此这里仅仅对遍历、删除和插入结点给出实现方法:
| |
| |
| bool InitList_Du(DuLinkList &L){ |
| L=(DuLNode*)malloc(sizeof(DuLNode)); |
| if(L==NULL) return false; |
| L->next=L; |
| L->pre=L; |
| return true; |
| } |
| |
| |
| void TraverseList_Du(DuLinkList L){ |
| int cnt=0; |
| DuLNode *p=L->next; |
| printf("head:"); |
| while(p!=L){ |
| if(cnt++) printf(" <-> "); |
| PrintData(p->data); |
| p=p->next; |
| } |
| printf("<->head\n"); |
| } |
| |
| int ListLength_Du(DuLinkList L){ |
| DuLNode *p=L->next; |
| int cnt=0; |
| while(p!=L){ |
| p=p->next; |
| cnt++; |
| } |
| return cnt; |
| } |
双向循环链表的插入

| |
| bool InsertList_i_Du(DuLinkList L,int i,Elem e){ |
| int len=ListLength_Du(L); |
| DuLNode *temp=(DuLNode*)malloc(sizeof(DuLNode)); |
| DuLNode *p=L; |
| if(i>len+1||i<=0||temp==NULL) |
| return false; |
| temp->data=e; |
| while(--i) |
| p=p->next; |
| temp->next=p->next; |
| temp->pre=p; |
| p->next->pre=temp; |
| p->next=temp; |
| return true; |
| } |
双向循环链表的删除

| |
| bool DeleteList_i_Du(DuLinkList L,int i,Elem &e){ |
| int len=ListLength_Du(L); |
| if(len==0||i>len||i<=0) |
| return false; |
| DuLNode* p=L; |
| while(--i) |
| p=p->next; |
| DuLNode *temp=p->next; |
| e=temp->data; |
| p->next=temp->next; |
| temp->next->pre=p; |
| free(temp); |
| return true; |
| } |
测试双向循环链表的main函数
此处用于测试的main函数调用了双向循环链表的插入和删除两个主要函数。
需要使用std命名空间,并且包含string头文件,以便于main函数内打印分割线。
| int main(void){ |
| string str="----------------------------------------------------------"; |
| Elem temp; |
| |
| DuLinkList L; |
| InitList_Du(L); |
| for(int i=1;i<=10;i++){ |
| temp.x=i; |
| InsertList_i_Du(L,1,temp); |
| } |
| cout<<str<<endl; |
| printf("链表长度:%d,链表结构如下:\n",ListLength_Du(L)); |
| TraverseList_Du(L); |
| |
| |
| cout<<str<<endl; |
| printf("删除第6个结点之后,结点数据为:"); |
| DeleteList_i_Du(L,6,temp); |
| PrintData(temp); putchar('\n'); |
| printf("删除后的链表长度为:%d,链表结构如下:\n",ListLength_Du(L)); |
| TraverseList_Du(L); |
| |
| cout<<str<<endl; |
| |
| return 0; |
| } |
输出结果如下:
| ---------------------------------------------------------- |
| 链表长度:10,链表结构如下: |
| head:10 <-> 9 <-> 8 <-> 7 <-> 6 <-> 5 <-> 4 <-> 3 <-> 2 <-> 1<->head |
| ---------------------------------------------------------- |
| 删除第6个结点之后,结点数据为:5 |
| 删除后的链表长度为:9,链表结构如下: |
| head:10 <-> 9 <-> 8 <-> 7 <-> 6 <-> 4 <-> 3 <-> 2 <-> 1<->head |
| ---------------------------------------------------------- |
Comments | NOTHING