单向动态链表
单项链表的一些必要声明
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
从表头到表尾逆向创建链表
图示:当链表为空时的插入情况
图示:当链表非空时的插入情况
/**
* 从表头到表尾逆向创建链表,包含n个节点
* 需传入表头指针的指针,因为需要对表头指针进行重定向
* 但凡其传入的参数需要改变原值的都需要传入其指针,包括指针本身
* 在p节点后插入in节点方法:in先和p指向相同的后继元,p再指向in
*/
void CreateList(LinkList * L, int n)
{
*L = (LinkList)malloc(sizeof(LNode)); /* 建立头结点 */
(*L)->next = NULL;
LinkList in;
for( ; n>0; n--) {
in = (LinkList)malloc(sizeof(LNode));
scanf("%d",&in->data); /* 创建新的节点 */
in->next = (*L)->next; /* 总是在头结点之后插入 */
(*L)->next = in;
}
}
从链表中获取第 i 个元素的数据
图示:链表为空,p一开始就指向NULL,不满足直接退出
图示:链表非空,while退出条件是ji,退出时,p刚好指向第i个节点
图示:链表非空,但i的位置为处没有节点,while退出条件是pNULL,不满足
/**
* 从链表中获取第 i 个元素的数据。
* 思路:可以从第一个元素开始p和j同步走,如果j=i满足退出,则p指向第i个节点
* 若是空表,则一开始p就为空了,直接返回error
* 若不是空表,但p到达了NULL,说明i不满足,也直接返回error
*/
Status GetElem(LinkList L, int i, ElemType *e)
{
LinkList p = L->next; /* p指向第一个元素 */
int j = 1; /* j从第 1 个元素开始 */
while (p && j<i) { /* 顺指针向后查找,直到p指向第i个元素或p为空 */
p = p->next;
j++;
}
if(!p || j>i)
return ERROR; /* 若p到达NULL,则必然没找到 */
*e = p->data;
return OK;
}
插入元素e到第i个位置
图示:链表为空时插入到第一个
图示:链表非空时,先将p索引到i-1下,然后在i-1后插入
图示:链表非空时,插入位置刚好在表尾巴
图示:链表非空时,插入位置超过了表尾
/**
* 在第i个位置前插入e
* 思路: 在第i个位置前插入,则插入后将取代i的位置,原来i的位置变成i+1
* 需先找打i-1的位置,同样让p和j同步,j=i-1时则p刚好指向i-1的位置
*/
Status ListInsert(LinkList L, int i, ElemType e)
{
LinkList p,s;
p = L; /* 从头结点开始 */
int j=0;
while (p && j<i-1) { /* 寻找第i-1个结点 */
p = p->next;
j++;
}
if(!p || j>i-1) /* i小于1或者大于表长加1*/
return ERROR;
s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next = p->next; /* s先和p指向相同的后继元 */
p->next = s; /* p再指向s */
return OK;
}
删除第i个位置的元素
图示:链表为空时没有元素可删除,显然不符合
图示:链表不为空时,p刚好索引到i-1,while退出条件是j==i-1正常删除
图示:链表不为空时,p刚好索引到i-1,while退出条件是p->next == NULL,则i处于NULL位置或更后面,i位置错误,不符合
图示:删除步骤
/**
* 删除第i个位置的元素
* 思路:先索引到第i-1,这时p指向i-1,然后让q指向i临时保存起来
* 然后断开i,即前一个不再指向它,而是指向它的后一个,最后将q释放
*/
Status ListDelete(LinkList L, int i, ElemType * e)
{
LinkList p,q;
p = L;
int j=0;
while(p->next && j<i-1) { /* 寻找第i个结点并令p指向其前趋 */
p = p->next;
j++;
}
if(!(p->next) || j>i-1) /* 删除位置不合理 */
return ERROR;
q = p->next; /* 先让q指向p的后继元即要删除的位置,保存起来 */
p->next = q->next; /* 将删除的位置断链,即前一个不再指向它,而是指向它的后一个 */
*e = q->data;
free(q);
return OK;
}
合并链表
图示:初始条件pa,pb指针其第一个节点,Lc和pc指向La头结点,比较 pa->data <= pb->data,满足则pc链接向pa的那个节点,同时pc移到pa位置处,pa指向下一个节点
图示:比较 pa->data <= pb->data,不满足则pc链接向pb的那个节点,同时pc移到pb位置处,pb指向下一个节点
/**
* 归并递增链表La和Lb得到同样递增链表Lc
* 思路:用三个指针pa,pb,pc,初始条件pa,pb指针其第一个节点,Lc和pc指向La头结点
* 比较 pa->data <= pb->data,满足则pc链接向pa的那个节点,pa指向下一个节点
* 让pa,pb指向剩余链表的首部,pc总是指向Lc的最后一个节点
*/
void MergeList(LinkList La, LinkList Lb, LinkList * Lc)
{
LinkList pa,pb,pc;
pa = La->next; /* pa,pb分别指向第一个结点 */
pb = Lb->next;
*Lc = pc = La; /* 用La的头结点作为Lc的头结点 */
while(pa && pb) {
if(pa->data <= pb->data) {
pc->next = pa; /* 链接小的那个 */
pc = pa;
pa = pa->next; /* 被链接的那个指向下一个 */
}
else {
pc->next = pb;
pc = pb;
pb = pb->next;
}
}
pc->next = pa ? pa : pb; /* 插入剩余段 */
free(Lb); /* 释放Lb的头结点 */
}
打印链表
void printList(LinkList L)
{
LinkList p = L->next; /* 指向第一个节点 */
while (p) { /* 不是空节点 */
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}