赞
踩
目录
本章介绍怎么使用数组模拟链表,首先得理解其定义
链表是指由一系列储存在非连续储存空间 结点组成的储存结构。每个结点由两部分组成:一是储存元素的数据域,一是储存下一个节点地址的指针域。用数组模拟链表得十分清晰明了地理解这一定义。
为什么要用数组模拟实现呢?数组实现的双链表本质上是静态链表,但是具有实现简单,速度极快等特点,比数据结构书上声明新的数据类型的方法代码简洁,好记忆。
数组是一种线性结构,存储空间是内存连续的,每当创建一个数组时,就必须先申请好一段指定大小的空间。由于内存连续这一特性,数组的访问特别快,知道下标索引后,就可以通过地址偏移计算得到指定位置的地址,进而能够直接访问到那个地址空间的数据。数组要保证内存连续这一特性,数组在进行任意位置的插入和删除元素时,就会涉及到部分元素的移动,即插入第i个位置时,需要把i+1位置及其后面的所有元素都往后移动一位,然后执行插入;或者删除第i个位置时,在删除后,需要把第i+1位置及其后面的所有元素都往前移动一位,这样才能保证数据的内存连续。
数组可以模拟实现单链表和双链表,我们先来介绍单链表。
代码如下
- int head, e[N], ne[N], idx;
- // head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点
-
- void init()//初始化
- {
- head = -1;idx = 0;
- }
-
- void insert(int a)// 在链表头插入一个数a
- {
- e[idx] = a,ne[idx] = head, head = idx ++ ;
- }
-
- void remove(int k)// 将k位置删掉
- {
- ne[k]=ne[ne[k]];
- }
- for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';//输出链表元素
-
- // e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
- int e[N], l[N], r[N], idx;
- void init()//初始化0是左端点,1是右端点
- {
- r[0] = 1, l[1] = 0;
- idx = 2;
- }
- void insert(int a, int x)// 在节点a的右边插入一个数x
- {
- e[idx] = x;
- l[idx] = a, r[idx] = r[a];
- l[r[a]] = idx, r[a] = idx ++ ;
- }
-
- void remove(int a)// 删除节点a
- {
- l[r[a]] = l[a];
- r[l[a]] = r[a];
- }
- insert(0,x);//插到头节点后;头节点下标是0
- insert(l[1],x);//插入到队尾,尾节点下标为1,所以尾节点的左节点下标为l[1]
- insert(l[k],x);//插入到k节点左边,k节点的左下标为了l[k]
- for (int i = r[0]; i != 1; i = r[i])cout << e[i] << ' ';//输出链表元素
-
图解代码
如果不用数组实现链表,代码的长度会很长,但是也有它的优点,好理解。
单链表代码如下
-
- #define ERROR 0
- #define OK 1
-
- typedef int Status;
- typedef int ElemType;
-
- struct Node{
- ElemType data;
- struct Node * next;
- };
-
- typedef struct Node *LinkList;
-
- void InitList(LinkList *L){
- *L = (LinkList)malloc(sizeof(Node));
- (*L)->next = NULL;
- }
-
- bool ListEmpty(LinkList L){
- if(L->next)
- return false;
- else
- return true;
- }
-
- void ClearList(LinkList *L){
- LinkList p,q;
- p = (*L)->next;
- while(p){
- q = p->next;
- free(p);
- p = q;
- }
- (*L)->next = NULL;
- }
-
- int ListLength(LinkList L){
- int i = 0;
- LinkList p = L->next;
- while(p){
- i++;
- p = p->next;
- }
- return i;
- }
-
- Status GetElem(LinkList L,int i,ElemType *e){
- int cnt = 1;
- LinkList p = L->next;
- while(p && cnt < i){
- p = p->next;
- cnt++;
- }
- if(!p)
- return ERROR;
- *e = p->data;
- return OK;
- }
-
- int LocateElem(LinkList L,ElemType e){
- int cnt = 0;
- LinkList p = L->next;
-
- while(p){
- cnt++;
- if(p->data == e)
- return cnt;
- p = p->next;
- }
-
- return 0;
- }
-
- Status ListInsert(LinkList *L,int i,ElemType e){
- LinkList p,s;
- p = (*L);
- int cnt = 1;
- while(p && cnt < i){
- cnt++;
- p = p->next;
- }
- if(!p)
- return ERROR;
- s = (LinkList)malloc(sizeof(Node));
- s->data = e;
- s->next = p->next;
- p->next = s;
- return OK;
- }
-
- Status ListDelete(LinkList *L,int i,ElemType *e){
- int cnt = 1;
- LinkList q,p;
- p = (*L);
- //此时 p 为头节点,p->next为第一个节点
- while( p->next && cnt < i){
- p = p->next;
- cnt++;
- }
- //第一个节点不为空,并且 cnt 小于要删除的节点位置,开始循环
- if(!(p->next))
- return ERROR;
- q = p->next;
- p->next = q->next;
- *e = q->data;
- free(q);
- return OK;
- }
-
- Status ListTraverse(LinkList L){
- LinkList p = L->next;
- while(p){
- printf("%d ",p->data);
- p = p->next;
- }
- printf("\n");
- return OK;
- }
-
- void CreateListHead(LinkList *L,int n){
- LinkList p;
- srand(time(0));
- *L = (LinkList)malloc(sizeof(Node));
- (*L)->next = NULL;
-
- for(int i = 0 ; i < n ; i++){
- p = (LinkList)malloc(sizeof(Node));
- p->data = rand()%100 + 1;
- p->next = (*L)->next;
- (*L)->next = p;
- }
- }
-
- void CreateListTail(LinkList *L,int n){
- LinkList p,r;
- srand(time(0));
- *L = (LinkList)malloc(sizeof(Node));
- r = *L;
- for(int i = 0 ; i < n ; i++){
- p = (LinkList)malloc(sizeof(Node));
- p->data = rand()%100 + 1;
- r->next = p;
- r = p;
- }
- r->next = NULL;
- }
-
-
对比发现代码长了非常多。这里只对比了单链表,如果有兴趣可以看看自行对比一下双链表
相较于数组模拟,数组更好写,更好记忆,如果参加一些比赛的话我更加推荐使用数组模拟的链表,另外C++ STL里的list,也是可以的。代码如下
- #include <list>
- list<int> a;
数组模拟链表经常作为算法的模板使用,要学会这个得了解基础链表的基本概念和实现方式,并且多写多用。如果不是很好理解的话可以尝试跟着代码画一遍图,就可以很好的理解了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。