文章目录
- 链表:
- 单链表的实现及操作:
- 1.指针描述的单链表L存储结构
- 2.查找L的第i个元素,将其值赋值给e
- 3.在L的第i个位置之前插入元素e
- 4.在L中,删除第i个元素,并返回其值e
- 5.输入n个元素,建立带头节点的单链表L
- 6.归并La和Lb的到新单链表Lc,按值非递减排列
- 链表操作实例及代码:
- 代码:
- 运行结果:
链表:
1.链式存储与顺序存储的不同:
顺序存储:逻辑关系上相邻的两元素,物理位置上也相邻。优点:可随机存取表中任一元素,存储位置可以用简单公式表示。缺点:插入删除需要移动大量元素。
链式存储:不要求逻辑关系上相邻的两元素,物理位置上也相邻。优缺点刚好和顺序存储的相反。
2.线性表的链式存储结构的特点:
用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的也可以是不连续的。
我们用结点存储一个数据元素,结点包含两个域,数据域存储数据元素的值,指针域存储直接后继。
链表每个结点中只包含一个指针域的,称为线性链表或单链表。
使用链表时,只需关心数据元素之间的逻辑顺序,无需关心每个数据元素在存储器中的实际位置。
单链表的实现及操作:
1.指针描述的单链表L存储结构
我们这里单链表是带有头结点的单链表,头结点是单链表第一个元素(首元结点)之前的结点,他的数据域可以不存任何信息,也可存链表长度等信息,他的指针域的指针指向第一个元素的结点。
单链表的头指针指向头结点,单链表可由头指针唯一确定,所以接下来我们都用结构指针描述。
如果线性表是空表,头结点的指针域为空,也就是指针指向null。
线性表最后一个结点的指针域里面的指针指向null。
LNode是一个结点类型,它里面有数据域还有指针域。
LinkList是结点指针类型,它可以通过提取结点的next成员指向我们的结点类型。
调用其结构体里面的成员,这里面就涉及到.
和->
操作符的区别了,A->B则A为指针,->是成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针。A.B则A为对象或者结构体。
接下来我们参数都是结构体指针LinkList,所以一律用->
提取结构体里面的成员。
next指针明显指向下一个LNode类型的结点,那么它就是个结构体指针。
头指针L指向头结点,L->next指向首元结点。
如果说L是头指针,那么L指向的是头结点,L->data是头结点的数据域,L->next是头结点的指针域。L->next指向头结点的下一个LNode类型的结点,也就是首元结点,L->next->data是首元结点的数据域,存的是第一个元素的值。
a指针指向b结点,用->提取a指针的成员,提取的是b这个结点的成员。可以看出我们根本没有考虑a指针所在结构体里面的数据域,也就是与a指针相对应的a的数据域里面的元素。也就是说,头指针仅仅充当了指针的作用,通过它,我们能够访问首元结点的成员。
typedef struct LNode
{LElemType_L data;struct LNode* next;
}LNode;
typedef LNode* LinkList;
2.查找L的第i个元素,将其值赋值给e
L是头指针,也就是指向头结点的指针,我们从第一个元素开始查找,所以要先通过头指针L找到指向第一个元素的指针,也就是L->next。方便起见我们用p表示指向第一个元素的指针。然后用一个j表示当前我们找到了第几个元素。p一开始指向第一个元素,所以j初始值赋为1。然后如果p指向null,意味着我们已经找完了链表里面全部元素,此时退出循环,如果i是不符合条件的,i<=0,那么也要退出循环。
退出循环之后我们判断一下,如果p指向null或者j>i,那么就error。反之,意味着是因为j=i而退出的循环,由于之前我们说p一开始指向第一个元素,所以j初始值赋为1,而且p指向下一个元素的同时j也加1,那么p可以认为始终指的是第j个元素。现在j指的i,也就是说p现在指的是第i个元素,那么把e赋给p->data即可。
Status GetElem_L(LinkList L, int i, LElemType_L &e)
{int j;LinkList p ;j = 1;p = L->next;while(p && j<i) //满足了才循环 {j++;p = p->next;}if(!p || j>i) return ERROR;e = p->data;return OK;
}
3.在L的第i个位置之前插入元素e
在第i个位置插入元素的话我们需要找到第i-1个元素的位置,然后将新结点放到第i-1个元素的位置的下一个位置,并将原来的第i个位置放到新结点的下一个位置。这里面插入可以插入到第一个元素之前,所以我们找的第i-1个元素的位置有可能是头结点的位置,那么我们遍历查找的时候就要从头结点开始,上一个函数我们说,p一开始指向第一个元素,所以j初始值赋为1。这里p指向头结点,所以j初值赋为0。然后继续循环。循环结束后判断退出的原因,是因为没找到还是i的值有问题,还是当j=i-1时退出得,满足j=i-1的话再进行插入。
插入一个结点,首先要给这个结点开一个空间,s指针指向这个新的空间,也就是s指针指向新的结点,这里插入的时候如果先把这个结点放到第i-1个结点的后面,那么我们就找不到原来的第i个结点了,因为原来的第i个结点是通过p->next来找到的,然鹅现在p->next指的是新的结点s。
所以我们就不能先更改p->next,而是先把p->next给连接到s的后面,然后再把p->next更新为s。
Status ListInsert_L(LinkList &L, int i, LElemType_L e)
{LinkList p, s;int j;p = L;j = 0; while(p && j<i-1) //寻找第i-1个结点 {p = p->next;++j;}if(!p || j>i-1)return ERROR;s = (LinkList)malloc(sizeof(LNode));if(!s)exit(OVERFLOW);s->data = e;s->next = p->next;p->next = s;return OK;
}
4.在L中,删除第i个元素,并返回其值e
删除第i个元素,意味着第i-1个元素连到第i+1个元素,我们知道第i-1个元素的位置,也就知道了后面的元素的位置,因此这里还是找第i-1个元素。
这里可以删除第一个元素,因此还是从头结点开始找。这里我们删除第i个元素的话,第i个元素一定要存在,所以判断里面要先判断第i-1个元素的下一个元素是否存在,本来我们是要找第i-1个元素,但是现在我们先查看了第i个元素,而且我们还存了第i-1个元素的位置,那么我们找第i个元素的话,即使退出循环,我们仍然能找到第i-1个元素的位置,这就转化成了,找第i个元素。当然你也可以找第i-1个元素,只不过你要判断,如果它后面没元素,就退出了,而并不是说只要第i-1个元素不为空,就能删。
最后退出了,但是pre存的是第i-1个元素的位置,那么pre->next=pre->next->next即可,这里删去之后还要把被删的结点的空间给释放了,所以先把pre->next存到p里面,然后释放p即可。
Status ListDelete_L(LinkList &L, int i, LElemType_L *e)
{LinkList pre, p; int j;pre = L;j = 1; while(pre->next && j<i) {pre = pre->next;++j;}if(!pre->next || j>i) return ERROR;p = pre->next;pre->next = p->next;*e = p->data;free(p);return OK;
}
5.输入n个元素,建立带头节点的单链表L
首先看头插法,也就是说先插入的在后面,每次插入是插到链表的第一个元素的位置。如果你输入9 8 7 6 5,链表从表头到表尾依次存放5 6 7 8 9。
头插法先要建立一个头结点,L指针指向头结点,L->next是首元结点,一开始是个空链表,所以L->next指向null。输入一个数,放到一个新结点里面,然后把这个结点插到原来的首元结点前面,也就是L->next前面,让他成为新的首元结点,然后由于L->next指向首元结点,所以我们更新L->next,让他指向这个新结点。
再看尾插法,也就是说先插入的在前面。
先建立头结点L,L->next就是第一个元素的位置,起初为null,我们需要引入一个标记q,让他一开始指向头结点,每当插入一个元素p,q的下一个位置就是p的位置,然后让它指向p的位置。这样每次新结点都放到了p后面,而且q又指向最后的那个元素的位置,那么也就说明我们每次都放到了最后的位置。
最终还需让q->next指向null,因为已经插入完了,它没有下一个元素了。
Status CreateList_HL(LinkList &L, int n)
{int i;LinkList p;LElemType_L tmp;L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL; printf("请输入%d个数:",n);for(i=1; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; p->next = L->next;L->next = p; }elsereturn ERROR;}return OK;
} Status CreateList_TL(LinkList &L, int n)
{int i;LinkList p, q;LElemType_L tmp; L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL;printf("请输入%d个数:",n);for(i=1,q=L; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; q->next = p;q = q->next; }elsereturn ERROR; }q->next = NULL;return OK;
}
6.归并La和Lb的到新单链表Lc,按值非递减排列
这个就是把La也当成Lc的头结点,然后从第一个元素开始,依次比较La,Lb的当前元素大小。如果小的,那么放到Lc的后面,然后把放过的那个元素所在的链表的指针后移一位。最后如果有一个列表已经遍历完了,那么就把那没被遍历完的列表放到Lc的后面,我们可以看出,Lb的元素全放到了La里面,最后只剩了一个Lb的头结点,所以我们把头结点free掉即可。
void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc)
{ LinkList pa, pb, pc;pa = La->next;pb = Lb->next;pc = Lc = La; 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 = NULL;
}
链表操作实例及代码:
代码:
#include<cstdio>
#include<cstdlib>#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define UNDERFLOW -3
#define TRUE 1
#define FALSE 0typedef int LElemType_L;
typedef int Status;typedef struct LNode
{LElemType_L data;struct LNode* next;
}LNode;
typedef LNode* LinkList;Status GetElem_L(LinkList L, int i, LElemType_L &e)
{int j;LinkList p = L->next;j = 1;p = L->next;while(p && j<i) {j++;p = p->next;}if(!p || j>i) return ERROR;e = p->data;return OK;
}
Status ListInsert_L(LinkList &L, int i, LElemType_L e)
{LinkList p, s;int j;p = L;j = 0; while(p && j<i-1) //寻找第i-1个结点 {p = p->next;++j;}if(!p || j>i-1)return ERROR;s = (LinkList)malloc(sizeof(LNode));if(!s)exit(OVERFLOW);s->data = e;s->next = p->next;p->next = s;return OK;
}
Status ListDelete_L(LinkList &L, int i, LElemType_L &e)
{LinkList pre, p; int j;pre = L;j = 1; while(pre->next && j<i) {pre = pre->next;++j;}if(!pre->next || j>i) return ERROR;p = pre->next;pre->next = p->next;e = p->data;free(p);return OK;
}Status CreateList_HL(LinkList &L, int n)
{int i;LinkList p;LElemType_L tmp;L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL; printf("(头插法)请输入%d个数:",n);for(i=1; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; p->next = L->next;L->next = p; }elsereturn ERROR;}return OK;
} Status CreateList_TL(LinkList &L, int n)
{int i;LinkList p, q;LElemType_L tmp; L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL;printf("(尾插法)请输入%d个数:",n);for(i=1,q=L; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; q->next = p;q = q->next; }elsereturn ERROR; }q->next = NULL;return OK;
}
Status ListTraverse_L(LinkList L, void(Visit)(LElemType_L))
{LinkList p;if(!L)return ERROR;elsep = L->next; while(p){Visit(p->data);p = p->next;}printf("\n");return OK;
}
void PrintElem(LElemType_L e)
{printf("%d ", e);
}
void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc)
{ LinkList pa, pb, pc;pa = La->next;pb = Lb->next;pc = Lc = La; 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 = NULL;
}
int main()
{LinkList La, Lb, Lc ;int m,i; LElemType_L e;m = 5;printf("作为示例,La长度设定为 %d ,Lb设定为 %d ,创建La和Lb...\n", m, m);CreateList_HL(La, m);CreateList_TL(Lb, m); printf("La = ");ListTraverse_L(La, PrintElem);printf("Lb = ");ListTraverse_L(Lb, PrintElem);printf("\n");MergeList_L(La, Lb, Lc);printf("合并La和Lb为Lc = ");ListTraverse_L(Lc, PrintElem);printf("\n");ListDelete_L(Lc, 6, e);printf("删除 Lc 中第 6 个元素 \"%d\" ...\n", e);printf(" Lc 中的元素为:Lc = "); ListTraverse_L(Lc, PrintElem);printf("\n");GetElem_L(Lc, 4, e);printf(" Lc 中第 4 个位置的元素为 \"%d\" \n", e);printf("\n");for(i=1; i<=6; i++) {printf("在 Lc 第 %d 个位置插入 \"%d\" ...\n", i, 2*i);ListInsert_L(Lc, i, 2*i);}printf(" Lc 中的元素为:Lc = "); ListTraverse_L(Lc, PrintElem);printf("\n");return 0;
}
运行结果:
输入:
9 7 5 3 1
2 4 6 8 10
输出:
作为示例,La长度设定为 5 ,Lb设定为 5 ,创建La和Lb…
(头插法)请输入5个数:9 7 5 3 1
(尾插法)请输入5个数:2 4 6 8 10
La = 1 3 5 7 9
Lb = 2 4 6 8 10
合并La和Lb为Lc = 1 2 3 4 5 6 7 8 9 10
删除 Lc 中第 6 个元素 “6” …
Lc 中的元素为:Lc = 1 2 3 4 5 7 8 9 10
Lc 中第 4 个位置的元素为 “4”
在 Lc 第 1 个位置插入 “2” …
在 Lc 第 2 个位置插入 “4” …
在 Lc 第 3 个位置插入 “6” …
在 Lc 第 4 个位置插入 “8” …
在 Lc 第 5 个位置插入 “10” …
在 Lc 第 6 个位置插入 “12” …
Lc 中的元素为:Lc = 2 4 6 8 10 12 1 2 3 4 5 7 8 9 10