前言
书接上文,前面叙述了线性表的顺序表和单链表,本文讲继续介绍线性表的有关知识。
双链表
在前面我们介绍过单链表,可知单链表的每个节点都会包含一个指向后继节点的指针,这样便于我们访问后继节点。但是我们在使用单链表时,经常要找到前一个节点。如果要找到尾节点,要遍历链表才能找到。双链表类似单链表,不同的是每一个节点不仅仅包含一个指向后继节点的指针,还含有一个指向前驱节点的指针,因此可以说它是双向的。
typedef struct DNode
{
ElemType data; //存放元素
struct DNode * pri; //指向前驱结点
struct DNode * next; //指向后继结点
}DLinkNode;
1.创建
类似单链表,双链表任然有头插法和尾插法
- 头插法
void CreatList(DLinkNode * &L, ElemType a[], int n)
{
int i;
DLinkNode * s;
L = (DLinkNode *)malloc(sizeof(DLinkNode));
L -> pri = NULL;
L -> next = NULL;
for(i = 0; i < n; i ++)
{
s = (DLinkNode *)malloc(sizeof(DLinkNode));
s -> data = a[i];
s -> next = L -> next;
if(L -> next != NULL)
{
L -> next -> pri = s;
}
L -> next = s;
s -> pri = L;
}
}
- 尾插法
void CreatList(DLinkNode * &L, ElemType a[], int n)
{
int i;
DLinkNode * s, r;
L = (DLinkNode *)malloc(sizeof(DLinkNode));
r = L;
for(i = 0; i < n; i ++)
{
s = (DLinkNode *)malloc(sizeof(DLinkNode));
s -> data = a[i];
r -> next = s;
s -> pri = r;
r = s;
}
r -> next = NULL;
}
2.插入
s结点
s = (DLinkNode *)malloc(sizeof(DLinkNode));
s -> data = e;
在p结点后插入s结点
p -> next = s -> next; //1
p -> next -> pri = s; //2
s -> pri = p; //3
p -> next = s; //4
3. 删除
找到要删除的结点q和他的前驱结点p
方法1
p -> next = p -> next;
q - > next -> pri = p;
方法2
p -> next = q -> next;
q -> next -> pri = p;
最后
free(q);
4. 基本运算
双链表其他运算于单链表类似,这里给出之前的链接
单链表
循环链表
循环链表即在单链表和双链表的基础上实现循环,即头结点和尾节点之间相连构成循环
其基本运算于对应的非循环链表基本相同,主要差别是对于链表来说判断其尾结点p的依据是
p -> next = L,同样可以通过L -> prior 找到尾结点
有序表
1.定义
线性表的元素按递增或递减的方式排列
2.基本运算算法
有序表的基本运算与前面的顺序表和链表大致相同,下面列出不同处
- 顺序表的插入
不同处在于插入要按顺序插入
找出插入位置:
while(i < L -> length && L -> data[i] < e) e为插入元素
i ++;
当找到要插入的位置后,须将此位置空出来,为此将i位置后的每一元素后移一位
for(j = L -> length; j > i; j --)
{
L -> data[j] = L -> data[j - 1];
}
实现i位置后的每一个元素后移一位,i位置空出
L -> data[i] = e;
L -> length ++;
- 链表的插入
找到要插入的位置
while(p -> next != NULL && p -> next -> data < e)
p = p -> next;
找到后直接插入
q = (LinkNode *)malloc(sizeof(LinkNode));
q -> data = e;
q -> next = p -> next;
p -> next = q;
3. 归并算法
现有两个有序表LA,LB,要求将其合并且不破坏LA,LB
思路:构建一个新的有序表LC,同时扫描LA和LB,将二者中较小的元素放入LC中,然后再从LC获取较小元素
的有序表中选取下一个元素继续比较,直到二者中有一个率先结束扫描,再将剩下元素放入LC中(二路归并)
以单链表为例
void UnionList(LinkNode * &LA, LinkNode * &LB, LinkNode * &LC)
{
LinkNode * pa =LA -> next, * pb = LB -> next; //pa,pb均是指首结点
LinkNode * r, *s;
LC = (LinkNode *)malloc(sizeof(LinkNode));
r = LC;
while(pa -> next != NULL && pb -> next != NULL)
{
if(pa -> data < pb -> data) //将两者较小的值插入到LC中
{
s = (LinkNode *)malloc(sizeof(LinkNode));
s -> data = pa -> data;
s-> next = r -> next;
r -> next = s;
pa = pa -> next;
}
else
{
s = (LinkNode *)malloc(sizeof(LinkNode));
s -> data = pb -> data;
s-> next = r -> next;
r -> next = s;
pb = pb -> next;
}
}
//跳出循环,将剩下的元素插入到LC中
while(pa != NULL) //将剩下的元素复制到LC中
{
s = (LinkNode *)malloc(sizeof(LinkNode));
s -> data = pa -> data;
s -> next = r -> next;
r -> next = s;
pa = pa -> next;
}
while(pb != NULL) //将剩下的元素复制到LC中
{
s = (LinkNode *)malloc(sizeof(LinkNode));
s -> data = pb -> data;
s -> next = r -> next;
r -> next = s;
pb = pb -> next;
}
r -> next = NULL;
}
小结
本文主要学习了双链表以及有序表,对照之前的单链表和顺序表,会发现其实并不难,同时也要注意到,在学习一种新的抽象结构中我们应该对照所学过的结构,这样学起来会很轻松
总结
本文连同上文简单的介绍了线性表的逻辑结构,两种存储结构,基本运算以及有序表,在学习的过程中不仅仅是认识了一种常见的线性结构,更加深了我们对于C语言指针和结构体的理解和掌握,为接下来学习栈和队列,奠定基础