线性表-单链表探究
2.3.1 单链表定义

1 2
| 单链表分为带头结点和不带头结点两种类型。在许多情况下,带头结点的单链表能够简化运算的实现过程。 因此这里讨论的单链表除特别指出外均指带头结点的单链表
|
单链表结构
1 2 3 4 5
| typedef struct node { ElemType data; struct node *next; } SLinkNode;
|
尾节点next探究- 1.指向null 2.指向头节点-循环单链表
2.3.2线性表基本运算在单链表上的
数据结构

1.初始化线性表运算算法
1 2 3 4 5 6 7
| 创建一个空的单链表,它只有一个头结点,由L指向它。该结点的next域为空,data域未设定任何值。对应的算法如下: void InitList(SLinkNode *&L) { L=(SLinkNode *)malloc(sizeof(SLinkNode)); L->next=NULL; }
|
2.销毁线性表运算算法
1
| 一个单链表中的所有结点空间都是通过malloc函数分配的,在不再需要时需通过free函数释放所有结点的空间。 循环next delete吗
|
1 2 3 4 5 6 7 8 9 10
| void DestroyList(SLinkNode *&L) { SLinkNode *pre=L,*p=pre->next; while (p!=NULL) { free(pre); pre=p; p=p->next; } free(pre);
}
|
3.求线性表的长度运算算法
1
| 设置一个整型变量i作为计数器,i初值为0,p初始时指向第一个数据结点。然后沿next域逐个往后查找,每移动一次,i值增1。当p所指结点为空时,结束这个过程,i之值即为表长。
|
1 2 3 4 5 6 7 8 9 10 11 12
| int GetLength(SLinkNode *L) { int=0; SLinkNode *p_next=L->next; while(p!=null) { i++; L=p_next; } return i; }
|
4.求线性表中第i个元素运算算法
1 2 3 4
| 用p从头开始遍历单链表L中的结点,用计数器j累计遍历过的结点,其初值为0。 在遍历时j等于i时,若p不为空,则p所指结点即为要找的结点,查找成功,算法返回1。 否则算法返回0表示未找到这样的结点。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| int GetElem(SLinkNode *L,int i,ElemType &e) { int j=0; SLinkNode *P=L; if(i<=0 ) { return 0; } while(p!=null &&j<I) { j++; p=p->next; } j-节点数量 -第i个元素 j=i; if(p==null) { return 0; }else { e=p->data; return 1; }
}
|
5.按值查找运算算法
1
| 在单链表L中从第一个数据结点开始查找第一个值域与e相等的结点,若存在这样的结点,则返回其逻辑序号;否则返回0。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int Locate(SLinkNode *L,ElemType e) { SLinkNode *p=L->next; int j=1; while(p!=null &&p->data!=e) { p=p.next; j++;
} if(p==null) { rerturn -1; } return j; }
|
6.插入元素运算算法
1 2 3 4
| 在单链表L中插入第i个值为x的结点。 先在单链表L中查找第i-1个结点,若未找到返回0; 找到后由p指向该结点,创建一个以x为值的新结点s,将其插入到p指结点之后。
|

头插
尾插
中间插
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int InsElem(SLinkNode *&L,ElemType x,int i) { int j=0; SLinkNode *p=L,*s; if (i<=0) return 0; while (p!=NULL && j<i-1) { j++; p=p->next; } if (p==NULL) return 0; else { s=(SLinkNode *)malloc(sizeof(SLinkNode)); s->data=x; s->next=p->next; p->next=s; return 1; } }
|
7.删除操作
中间删

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int DelElem(SLinkNode *&L,int i) { int j=0; SLinkNode *p=L,*q; if (i<=0) return 0; while (p!=NULL && j<i-1) { j++; p=p->next; } if (p==NULL) return 0; else { q=p->next; if (q==NULL) return 0; else { p->next=q->next; free(q); return 1; } } }
|
8.输出线性表运算算法
1
| 从第一个数据结点开始,沿next域逐个往下遍历,输出每个遍历到结点的data域,直到尾结点为止。
|
1 2 3 4 5 6 7 8 9
| void DispList(SLinkNode *L) { SLinkNode *p=L->next; while (p!=NULL) { printf("%d ",p->data); p=p->next; } printf("\n"); }
|
整体创建单链表的算法
可以通过调用基本运算算法来创建单链表,其过程是先初始化一个单链表,然后向其中一个一个地插入元素。
这里介绍是快速创建整个单链表的算法,也称为整体建表。
假设给定一个含有n个元素的数组a,由它来创建单链表,这种建立单链表的常用方法有两种。
1.头插
1 2 3 4 5
| 从一个空单链表(含有一个L指向的头结点)开始。 读取数组a(含有n个元素)中的一个元素,生成一个新结点s,将读取的数据元素存放到新结点的数据域中。 将新结点s插入到当前链表的表头上。 再读取数组a的下一个元素,采用相同的操作建立新结点s并插入到单链表L中,直到数组a中所有元素读完为止。
|
1 2 3 4 5 6 7 8 9 10 11
| void CreateListF(SLinkNode *&L,ElemType a[],int n) { SLinkNode *s; int i; L=(SLinkNode *)malloc(sizeof(SLinkNode)); L->next=NULL; for (i=0;i<n;i++) { s=(SLinkNode *)malloc(sizeof(SLinkNode)); s->data=a[i]; s->next=L->next; L->next=s; } }
|

2.尾插
1 2 3 4 5 6
| 从一个空单链表(含有一个L指向的头结点)开始。 读取数组a(含有n个元素)中的一个元素,生成一个新结点s,将读取的数据元素存放到新结点的数据域中。 将新结点s插入到当前链表的表尾上。 再读取数组a的下一个元素,采用相同的操作建立新结点s并插入到单链表L中,直到数组a中所有元素读完为止。 由于尾插法算法每次将新结点插到当前链表的表尾上,为此增加一个尾指针tc,使其始终指向当前链表的尾结点。
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| void CreateListR(SLinkNode *&L,ElemType a[],int n) { SLinkNode *s,*tc; int i; L=(SLinkNode *)malloc(sizeof(SLinkNode)); tc=L; for (i=0;i<n;i++) { s=(SLinkNode *)malloc(sizeof(SLinkNode)); s->data=a[i]; tc->next=s; tc=s; } tc->next=NULL; }
|

单链表算法示例
1. 基于单链表基本操作的算法设计
2.11
2.13
2. 基于整体建表的算法设计
1 2 3
| 这类算法设计中需要根据条件产生新的结果单链表。 而创建结果单链表的方法有头插法和尾插法。
|
2.14
3. 有序单链表的二路归并算法
1 2 3
| 有序单链表是有序表的单链表存储结构,同样可以利用有序表元素的有序性提高相关算法的效率。 当数据采用单链表存储时,对应的二路归并就是单链表二路归并算法。
|
2.16
4. 单链表的排序
2.18
循环单链表
2.3.4.1 循环单链表定义

2.3.1.2 算法定义
1.初始化线性表运算算法
1
| 创建一个空的循环单链表,它只有头结点,由L指向它。该结点的next域指向该头结点,data域未设定任何值。
|
1 2 3 4 5
| void InitList(SLinkNode *&L) { L=(SLinkNode *)malloc(sizeof(SLinkNode)); L->next=L; }
|
2.销毁线性表运算算法
1
| 一个循环单链表中的所有结点空间都是通过malloc函数分配的,在不再需要时需通过free函数释放所有结点的空间。
|
1 2 3 4 5 6 7 8 9
| void DestroyList(SLinkNode *&L) { SLinkNode *pre=L,*p=pre->next; while (p!=L) { free(pre); pre=p; p=p->next; } free(pre); }
|
3.求线性表的长度运算算法
1
| 设置一个整型变量i作为计数器,i初值为0,p初始时指向第一个结点。然后沿next域逐个往下移动,每移动一次,i值增1。当p所指结点为头结点时这一过程结束,i之值即为表长。
|
1 2 3 4 5 6 7 8 9 10
| int GetLength(SLinkNode *L) { int i=0; SLinkNode *p=L->next; while (p!=L) { i++; p=p->next; } return i; }
|
4.求线性表中第i个元素运算算法
1 2 3 4
| 用p从头开始遍历循环单链表L中的结点(初值指向第一个数据结点),用计数器j累计遍历过的结点,其初值为1。 当p不为L且j<i时循环,p后移一个结点,j增1。 当循环结束时,若p指向头结点则表示查找失败返回0,否则p所指结点即为要找的结点,查找成功,算法返回1。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int GetElem(SLinkNode *L,int i,ElemType &e) { int j=1; SLinkNode *p=L->next; if (i<=0) return 0; while (p!=L && j<i) { j++; p=p->next; } if (p==L) return 0; else { e=p->data; return 1; } }
|
5.按值查找运算算法
1 2 3
| 用i累计查找数据结点的个数,从第一个数据结点开始,由前往后依次比较单链表中各结点数据域的值。 若某结点数据域的值等于给定值x,则返回i;否则继续向后比较。 若整个单链表中没有这样的结点,则返回0。
|
1 2 3 4 5 6 7 8 9 10 11 12
| int Locate(SLinkNode *L,ElemType x) { int i=1; SLinkNode *p=L->next; while (p!=L && p->data!=x) { p=p->next; i++; } if (p==L) return 0; else return i; }
|
6.插入元素运算算法
1 2 3 4
| 在循环单链表L中查找第i个结点p及其前驱结点pre。 若没有这样的结点p返回0。 否则创建一个以x为值的新结点s,将结点s插入在pre结点之后,返回1。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int InsElem(SLinkNode *&L,ElemType x,int i) { int j=1; SLinkNode *pre=L,*p=pre->next,*s; if (i<=0) return 0; while (p!=L && j<i) { j++; pre=p; p=p->next; } if (p==L && i>j+1) return 0; else { s=(SLinkNode *)malloc(sizeof(SLinkNode)); s->data=x; s->next=pre->next; pre->next=s; return 1; } }
|
1 2 3
| 在循环链表中,用p指针扫描所有结点时,方式有两种: 以p!=L作为循环条件,当p==L时循环结束,此时p回过来指向头结点,所以p应该初始化指向第一个数据结点而不是头结点,否则循环内的语句不会执行。 扫描指针p的初始化为p=L,循环的条件应该为p->next!=L,当p->next==L时循环结束,此时p指向尾结点
|
7.删除元素运算算法
1 2
| 在循环单链表L中查找第i-1个结点,若不存在这样的结点返回0。 否则让p指第i-1个结点,q指向后继结点,当q为NULL时返回0,否则将q所指结点删除并释放其空间,返回1
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int DelElem(SLinkNode *&L,int i) { int j=0; SLinkNode *p=L,*q; if (i<=0) return 0; while (p->next!=L && j<i-1) { j++; p=p->next; } if (p->next==L) return 0; else { q=p->next; if (q==L) return 0; else { p->next=q->next; free(q); return 1; } } }
|
8.输出线性表运算算法
1
| 从第一个数据结点开始,沿next域逐个往下遍历,输出每个遍历到结点的data域,直到头结点为止。
|
1 2 3 4 5 6 7 8 9
| void DispList(SLinkNode *L) { SLinkNode *p=L->next; while (p!=L) { printf("%d ",p->data); p=p->next; } printf("\n"); }
|
2.3.5 循环单链表算法
2.19
2.20
2.21