数据结构与算法系列研究一——链表
数据结构与算法之线性表
人的记忆都是有选择性的,面对着大量的知识,我们更擅长于感性记忆,而对于算法和数据结构这些逻辑性很强的东西如果不是刻意的记忆或者使用渐渐地就会觉得无力为继,这里是我两年前学过的一些算法的总结,这些算法从最基本的线性表开始,不断的深入,将链表、栈、队列、二叉树、图论等数据结构和相应的算法进行归纳和总结,一直觉得只是纯理论的东西哪怕就是写的再好使用价值也只是停留在纸上谈兵,没有自己认真的编写过代码,调试过程序,一切都是在糊弄自己罢了,因此这里的每一种基础知识都将配合算法来进行演示和说明,在算法中去体会程序的魅力。
一、数据和算法的本质
数据是客观事物的符号表示,是所有能输入计算机中并被计算机处理的符号的总称。在计算机科学中,数据的含义极为广泛,图象、声音等都可以通过编码存入到计算机中,也属于数据的范畴。数据元素是数据的基本单位,通常作为一个整体。数据项是组成数据元素,数据的不可分割的最小单位。数据对象是性质相同的数据元素的集合,是某种类型数据的子集。算法是对特定问题求解步骤的一种描述,是指令的有穷序列,其中每条指令表示一个或多个操作。算法有 有穷性、确定性、可行性、输入、输出这五种特性。
二、线性表
在数据元素的非空有限集中:
1、存在唯一的一个被称为“第一个”的数据元素;
2、存在唯一的一个被称为“最后一个”的数据元素;
3、除第一个之外,集合中的每个数据元素有且仅有一个直接前驱;
4、除最后一个之外,集合中的每个数据元素有且仅有一个直接后继。
线性表是n个数据元素的有限序列。记为:
D={a1,a2,……an}
ai是一个抽象符号。同一线性表中元素具有相同性质。
相邻数据元素间存在着序偶关系,可表示为:
R={〈ai-1,ai〉│i=2..n }
〈ai-1,ai〉表示ai-1是ai直接前驱,ai是ai-1的直接后继。
线性表的长度:表中数据元素个数(n≥0) 。
n=0表示空表。
在非空表,每个元素都有确定的位置。ai是第i个元素,称i为ai在线性表中的位序。
元素间关系与元素具体内容无关。
ADT List { 数据对象:D
数据关系;R
基本操作:
InitList(&L): 初始化一个线性表
DestroyList(&L): 撤消线性表
ClearList(&L): 表置空操作
ListEmpty(L): 判空表函数
ListLength(L): 求线性表长度的函数
GetElem(L,i,&e): 取第个i数据元素
LocateElem(L,x): 定位函数
PriorElem(L,elm,&pre_e): 求元素elm的前驱
NextElem(L,elm,&next_e): 求元素elm的后继
ListInsert(&L,i,e): 在第个i元素之前插入元素e
ListDelete(&L,i,&e): 删除第i个元素
Listtraverse(L): 遍历线性表元素
} ADT List
三、线性表实现归并排序
1 #include"stdlib.h"
2 #include"iostream.h"
3 #include"stdio.h"
4 #include"string.h"
5 /*****************************/
6 #define LISTMAXSIZE 100 //定义最大容量
7 typedef int status ; //用status代替int的表示
8 typedef int ElemType; //用ElemType代替int的表示
9 typedef struct {
10 ElemType elem[LISTMAXSIZE];//定义数组
11 ElemType Length; //定义当前长度
12 } SqList;
13 /*****************************/
14 /****************归并排序函数*********************/
15 status MergeSort(SqList &List1,SqList &List2,SqList &List3)
16 {
17 int i=0,j=0,k=0;
18 if(List1.Length+List2.Length> LISTMAXSIZE)
19 return 0; //若长度超出范围则失败
20 while((i<List1.Length)&&(j<List2.Length))
21 if(List1.elem[i]<=List2.elem[j])
22 List3.elem[k++]=List1.elem[i++];
23 else
24 List3.elem[k++]=List2.elem[j++];
25 while(i<List1.Length)
26 List3.elem[k++]=List1.elem[i++]; //若List1有剩余,将剩余部分移到后面
27 while(j<List2.Length)
28 List3.elem[k++]=List2.elem[j++]; //若List2有剩余,将剩余部分移到后面
29 List3.Length=k; //获得List3的长度
30 return 1;
31 }
32 /***********************************************/
33 /*******************初始化函数*****************/
34 void InitList(SqList &List1,SqList &List2,SqList &List3)
35 {
36 for(int i=0;i<10;i++)
37 List1.elem[i]=2*i+1; //按升序排列
38 List1.Length=10;
39 for(int j=0;j<13;j++)
40 List2.elem[j]=5*(j-3); //按升序排列
41 List2.Length=13;
42 memset(List3.elem,0,LISTMAXSIZE); //清零
43 List3.Length=0;
44 }
45 /************************************************/
46 /***********************打印函数********************/
47 void print(SqList &List)
48 {
49 for(int i=1;i<=List.Length;i++)
50 if(i%5!=0)
51 printf("%d ",List.elem[i-1]); //按每行5个的形式输出
52 else
53 printf("%d\n",List.elem[i-1]);
54 printf("\n");
55 }
56 /****************************************************/
57 int main()
58 {
59 SqList SqList1,SqList2,SqList3;
60 InitList(SqList1,SqList2,SqList3);
61 if(MergeSort(SqList1,SqList2,SqList3))
62 {
63 printf("LIST1:\n");
64 print(SqList1);
65 printf("\nLIST2:\n");
66 print(SqList2);
67 printf("\nAfter Merge:\n");
68 print(SqList3);
69 }
70 else
71 printf("超出范围,归并失败");
72 return 0;
73 }
截图如下:
四、插入或删除链表元素实现
1 #include"stdio.h"
2 #include"stdlib.h"
3 typedef int ElemType;
4 typedef struct SqList{
5 ElemType elem;
6 int length;
7 struct SqList *next;
8 }List,*LinkList;
9 int length=10; //定义整个链表的长度
10 //采用带头节点的头插法创建链表
11 LinkList CreateList( )
12 {
13 LinkList head;
14 head=0;
15 LinkList p;
16 for(int i=0;i<10;i++)
17 {
18 p=(LinkList)malloc(sizeof(List));
19 p->elem= 3*i-4;
20 p->next=head;
21 head=p;
22 }
23 return head;
24 }
25 //打印出链表的元素
26 void PrintList(LinkList head)
27 {
28 LinkList p=head;
29 for(LinkList q=p;q;q=q->next)
30 {
31 printf("%d ",q->elem);
32 }
33 printf("\n");
34 }
35 /*********插入数据的算法***************************/
36 LinkList Insert(LinkList head,int i,int num)
37 {
38 LinkList p=(LinkList)malloc(sizeof(List)),q=head;
39 int j;
40 p->elem=num;
41 if(i==1)//若在开头
42 {
43 p->next=head;
44 head=p;
45 }
46 else
47 if(i==length+1)//若在结尾
48 {
49 for(j=1;j<=length-1;j++)
50 q=q->next;
51 q->next=p;
52 p->next=0;
53 }
54 else if(i>1||i<=length)//若在中间
55 {
56 for(j=1;j<i-1;j++)
57 q=q->next;
58 p->next=q->next;
59 q->next=p;
60 }
61 else
62 {
63 printf("输入位置有错误!");
64 return head;
65 }
66 return head;
67 }
68 /******************删除数据的算法******************/
69 LinkList Delete(LinkList head,int i)
70 {
71 LinkList p=head,q;
72 int j;
73 if(i<=0||i>length)
74 {
75 printf("操作失败");
76 return head;
77 }
78 if(i==1)//若在开头
79 {
80 p=head;
81 head=head->next;
82 free(p);
83 }
84 else if(i==length)//若在结尾
85 {
86 for(j=1;j<i-1;j++)
87 {
88 p=p->next;
89 }
90 q=p->next;
91 p->next=0;
92 free(q);
93 }
94 else if(i<length&&i>1)//若在中间
95 {
96 for(j=1;j<i-1;j++)
97 {
98 p=p->next;
99 }
100 q=p->next;
101 p->next=q->next;
102 free(q);
103 }
104 length--;//长度自减
105 return head;
106 }
107
108 /****************核心算法结束*****************/
109
110 int main()
111 {
112 LinkList h=CreateList();
113 printf("原来链表为:\n");
114 PrintList(h);
115 h=Insert(h,11,12);
116 printf("插入元素后链表为:\n");
117 PrintList(h);
118 h=Delete(h,1);
119 printf("删除元素后链表为:\n");
120 PrintList(h);
121 return 0;
122 }
五、删除链表中指定范围的元素
比如(min,max),则将删除min<=x<=max之间的所有元素。
#include"stdio.h"
#include"stdlib.h"
typedef int ElemType;
typedef struct SqList{
ElemType elem;
int length;
struct SqList *next;
}List,*LinkList;
int length=10,flag=1; //定义整个链表的长度
//采用带头节点的头插法创建链表
LinkList CreateList( )
{
LinkList head;
head=0;
LinkList p;
for(int i=0;i<10;i++)
{
p=(LinkList)malloc(sizeof(List));
p->elem= -5*i+100;
p->next=head;
head=p;
}
return head;
}
//打印出链表的元素
void PrintList(LinkList head)
{
LinkList p=head;
for(LinkList q=p;q;q=q->next)
{
printf("%d ",q->elem);
}
printf("\n");
}
/******************删除数据的算法******************/
LinkList Delete(LinkList head,LinkList r)
{
LinkList p=head,q,r1=r;
if(head==r1)
{
head=head->next;
free(r1);
flag=0;
}
else
{
for(q=p;q->next!=r;q=q->next)
;
p=q->next;
q->next=p->next;
free(p);
flag=1;
}
length--;//长度自减
return head;
}
/************核心算法****************/
LinkList DeleteField(LinkList head,int mink,int maxk)
{
LinkList q,p=head;
for(q=head;q;q=q->next)
{
q=p;
if((q->elem>mink)&&(q->elem<maxk))
{
head=Delete(head,q);
q=head;
if(flag==0)
{
p=q;
continue;
}
}
if(length==0)
break;
p=p->next;
}
return head;
}
/****************核心算法结束*****************/
int main()
{
LinkList h=CreateList();
printf("原来链表为:\n");
PrintList(h);
h=DeleteField(h,2,67);
printf("\n删除后链表为:\n");
PrintList(h);
return 0;
}
运行结果:
六、链表实现归并排序
1 #include"stdio.h"
2 #include"stdlib.h"
3 typedef int ElemType;
4 typedef struct SqList{
5 ElemType elem;
6 int length;
7 struct SqList *next;
8 }List,*LinkList;
9 int length=10,flag=1; //定义整个链表的长度
10 //采用带头节点的头插法创建链表
11 LinkList CreateList1( )
12 {
13 LinkList head;
14 head=0;
15 LinkList p;
16 for(int i=0;i<10;i++)
17 {
18 p=(LinkList)malloc(sizeof(List));
19 p->elem= -5*i+100;
20 p->next=head;
21 head=p;
22 }
23 return head;
24 }
25 LinkList CreateList2( )
26 {
27 LinkList head;
28 head=0;
29 LinkList p;
30 for(int i=0;i<15;i++)
31 {
32 p=(LinkList)malloc(sizeof(List));
33 p->elem= 3*i+15;
34 p->next=head;
35 head=p;
36 }
37 return head;
38 }
39
40 //打印出链表的元素
41 void PrintList(LinkList head)
42 {
43 LinkList p=head;
44 int i=0;
45 for(LinkList q=p;q;q=q->next)
46 {
47 if((++i)%10!=0)
48 printf("%d ",q->elem);
49 else
50 printf("%d \n",q->elem);
51 }
52 printf("\n");
53 }
54 /*********插入数据的算法***************************/
55 LinkList Insert2T1(LinkList head1,LinkList head2)
56 {
57 LinkList r,q=head1,p,p2=head2;
58 for(r=q;r->next!=0; )
59 {
60 p=(LinkList)malloc(sizeof(List));
61 p->elem=p2->elem;
62 p2=p2->next;
63 p->next=r->next;
64 r->next=p;
65 r=p->next;
66 }
67 while(p2)
68 {
69 p=(LinkList)malloc(sizeof(List));
70 p->elem=p2->elem;
71 p2=p2->next;
72 r->next=p;
73 p->next=0;
74 r=p;
75 }
76 return head1;
77 }
78 /****************核心算法结束*****************/
79 int main()
80 {
81 LinkList h1=CreateList1(),h2=CreateList2();
82 printf("原来链表A为:\n");
83 PrintList(h1);
84 printf("原来链表B为:\n");
85 PrintList(h2);
86 printf("B插入A中合并为:\n");
87 h1=Insert2T1(h1,h2);
88 PrintList(h1);
89 return 0;
90 }
运行结果:
七、求单向循环链表节点p的前驱结点*q
1 //已知一单项循环链表某节点地址p,写一个算法求节点*p的前驱结点地址
2 #include"stdio.h"
3 #include"stdlib.h"
4 typedef int ElemType;
5 typedef struct SqList{
6 ElemType elem;
7 struct SqList *next;
8 }List,*LinkList;
9 //采用不带头节点的头插法创建链表
10 LinkList CreateList( )
11 {
12 LinkList head=NULL;
13 LinkList p,q;
14
15 for(int i=0;i<10;i++)
16 {
17 p=(LinkList)malloc(sizeof(List));
18 p->elem= 3*i-4;
19 if(i==0)
20 {
21 q=p;
22 }
23 p->next=head;
24 head=p;
25 }
26 q->next=head;
27 return head;
28 }
29 //算法实现前驱结点的地址
30 LinkList GetFront(LinkList p)
31 {
32 LinkList q;
33 for(q=p->next;q->next->elem!=p->elem;q=q->next)
34 ;
35 return q;
36 }
37 //打印出链表的元素
38 void PrintList(LinkList head)
39 {
40 LinkList p=head;
41 for(LinkList q=p;q->next!=p;q=q->next)
42 {
43 printf("%d ",q->elem);
44 }
45 printf("%d",q->elem);
46 }
47 int main()
48 {
49 LinkList h=CreateList(),p;
50 PrintList(h);
51 p=GetFront(h->next->next);
52 printf("\n头节点下一个元素是:\n");
53 printf(" %d ",p->elem );
54 printf("其地址为: %d\n",p);
55 return 0;
56 }
结果截图如下:
八、完善双向链表
1 #include"stdio.h"
2 #include"stdlib.h"
3 typedef int ElemType;
4 typedef int status;
5 typedef struct DulList{
6 ElemType elem;
7 struct DulList *next,*prior;
8 int length;
9 }*DulLink,DulLinkList;
10 /*************************/
11 //初始化链表,需要注意的是传指针的地址
12 void InitList(DulLink * l)
13 {
14 (*l)=(DulLink)malloc(sizeof(DulLinkList));
15 (*l)->prior=(*l)->next=(*l);
16 (*l)->length=0;
17 }
18 /******************************/
19 //核心算法构建链表,亦即连接prior和next
20 void CreateList(DulLink L,int n)
21 {
22 DulLink q=L,p;
23 for(int i=0;i<n;i++)
24 {
25 p=(DulLink)malloc(sizeof(DulLinkList));
26 p->elem=2*i+1;
27 L->length++;
28
29 q->next=p;//使上一个节点与下一个节点连接
30 p->prior=q;//填充prior域
31 p->next=L;//新增节点next域指向表头
32 L->prior=p;//表头prior域指向新增节点
33 q=p; //q指向新节点
34 }
35 }
36 /***************核心算法结束***************/
37 //打印链表
38 void PrintList(DulLink L)
39 {
40 DulLink p;
41 if(L==NULL)
42 return ;
43 for(p=L->next;p->next!=L;p=p->next)
44 {
45 printf("%d ",p->elem);
46 }
47 printf("%d \n",p->elem);
48 }
49
50 int main()
51 {
52 DulLink L=NULL;
53 int n;
54 printf("请输入创建节点数:\n");
55 scanf("%d",&n);
56 InitList(&L);
57 CreateList(L,n);
58 PrintList(L);
59 return 0;
60 }
运行结果:
九、将单向链表奇数偶数位元素交换
1 #include"stdio.h"
2 #include"stdlib.h"
3 typedef int ElemType;
4 typedef struct SqList{
5 ElemType elem;
6 int length;
7 struct SqList *next;
8 }List,*LinkList;
9 //采用带头节点的头插法创建链表
10 LinkList CreateList( )
11 {
12 LinkList head=(LinkList)malloc(sizeof(List));
13 head->next=0;
14 head->length=0;
15 LinkList p;
16 for(int i=0;i<10;i++)
17 {
18 p=(LinkList)malloc(sizeof(List));
19 p->elem= 3*i-4;
20 p->next=head->next;
21 head->next=p;
22 head->length++;
23 }
24 return head;
25 }
26 //打印出链表的元素
27 void PrintList(LinkList head)
28 {
29 LinkList p=head->next;
30 for(LinkList q=p;q;q=q->next)
31 {
32 printf("%d ",q->elem);
33 }
34 }
35 /*************此处为核心代码功能是实现奇偶转换*************/
36 LinkList ChangeElem(LinkList head)
37 {
38 LinkList p=head->next,q=p->next,r;//保证每次以从左到右r,p,q排列
39 if((head->length==1)||(head->length==0))
40 return head;
41 if((head->length)%2==0)//若长度为偶数项,则成对转换
42 {
43 if(p==head->next)//从开头开始
44 {
45 p->next=q->next;
46 head->next=q;
47 q->next=p;
48 }
49 while(p->next!=0)
50 {
51 r=p;
52 p=p->next;
53 q=p->next;//保证每次以从左到右r,p,q排列,进行新一对的转换
54 /*************转换核心******************/
55 p->next=q->next;
56 r->next=q;
57 q->next=p;
58 /**********转换结束********************/
59 }
60 }
61 else//若长度为奇数项,则成对转换
62 {
63 if(p==head->next)
64 {
65 p->next=q->next;
66 head->next=q;
67 q->next=p;
68 }
69 //结束标志为p的下一位指针域为0
70 while(p->next->next!=0)
71 {
72 r=p;
73 p=p->next;
74 q=p->next;//保证每次以从左到右r,p,q排列,进行新一对的转换
75 /*************转换核心******************/
76 p->next=q->next;
77 r->next=q;
78 q->next=p;
79 /**********转换结束********************/
80 }
81 }
82 return head;
83 }
84
85 /****************核心算法结束*****************/
86
87 int main()
88 {
89 LinkList h=CreateList(),h1;
90 printf("原来链表为:\n");
91 PrintList(h);
92 printf("\n修改后链表为:\n");
93 h1=ChangeElem(h);
94 PrintList(h1);
95 printf("\n");
96 return 0;
97 }
运行结果:
链表为奇数个时:
链表为偶数个时:
通过这一章,我们将线性表中的顺序存储结构——数组和链式存储结构——链表的增删改查有了一定的了解和使用,通过编程的方法强化了自己的知识储备,为以后的生活和工作奠定了良好的理论基础和能力积淀。