结构体链表与数组模拟链表

        链表分为单链表、双向链表和循环链表,这三种数据结构本质上就是指针的灵活使用,只要理解了单链表的一些操作,那么剩下两种也能快速掌握。单链表的每一个节点不同于数组是连续的,每一个节点都考前一个节点确定后才能确定

        单链表通常由一个头指针指向单链表的第一个节点,每个节点通常是一个结构体,结构体内部包含一个指针,该指针指向下一个地址。在初始化创建单链表时有两种方式,一种是不包含头结点的(上方),一种是包含头结点的(下方)。

         头结点与普通的节点使用同一种结构体但只是用指针,通常采用带头节点的方式,这样在插入删除时,不需要使用分支结构来判断是否要用到头结点。

单链表初始化

        以最基本的结构体为例,内部只包含整形元素和指针,将Head传入初始化函数中以引用的形式接收,让头指针连接头结点并初始化指针,完成单链表的初始化。最终形成的逻辑结构与上图中第二个结构一样,不包含节点一。

typedef struct List {
	int data;
	struct List* next;
}List;

void Init_List(List* &Head)
{
	Head = new List();
	Head->next = NULL;
}

int main()
{
	List* Head = NULL;
	Init_List(Head);
}

单链表插入节点 

        如果不需要注意数据的顺序,可以采用头插法,直接放入头指针后面即可,这样效率最高。如果需要按照一定顺序或者插入某一个位置,需要一个遍历指针来找插入位置的前驱。如下图所示,假设需要在节点1与节点2之间插入一个新节点,那么需要让指针指向插入位置的前驱,然后让新节点的指针指向后继,在让其被连接。

         新节点需要先连接后继再被前驱连接,该顺序不能交换。因为节点2是靠节点1找到的,而节点1是遍历过程中自定义的指针指向的。一旦先指向新节点,那么节点2及其后面的所有节点都无法再访问到了。如果选择两个相邻的指针一起后移,使得节点12都被指向,那么顺序就无所谓了。

void Insert_List(List *Head, int n, int e)	//在第n位插入数据e
{
	List *p = Head;
	for (int i = 1; i < n; i++)
		p = p->next;
	List* node = new List({e, p->next});
	p->next = node;
}

  带头结点的优势

        如果所需要插入的节点是在第一个,含有头结点的单链表依然可以用上面的代码运行不需要用到头指针,而不带头结点的单链表用不到额外的指针,需要用到头指针的。两者的指针不同因此需要用分支来进行判断。

 单链表删除节点

        删除节点需要用到相邻两个指针,靠右的指针指向被删除的节点,当右指针找到删除节点后,左指针直接指向右指针的后继即可,然后将右指针指向的节点删除。

         在插入节点时采用这样的方式也可忽略顺序,最本质的原因是是否会丢失原有的节点。不含头结点的单链表在删除第一个节点时也存在同样的问题,其只需要将头指针移动即可,不需要新指针因此依然需要有分支。下面代码中pre指向的是节点1,p指向头结点,随着循环进行逐步向后查找,以pre为空表示没找到要删除的节点,代码中未给出该情况下的措施,可能会出现删除的第n个节点超过了节点总数。

void Delete_List(List* Head, int n, int &retn)	//删除第n位节点并返回值
{
	List *pre = Head->next, *p = Head;
	for (int i = 1; i < n && pre != NULL; i++)
	{
		p = pre;
		pre = pre->next;
	}

	p->next = pre->next;
	retn = pre->data;
	delete pre;
}

单链表查询

        获得头结点的地址后第一新的指针往后遍历至NULL即可。

void Print_List(List* Head)
{
	List* p = Head->next;
	while (p != NULL)
	{
		cout << p->data << ' ';
		p = p->next;
	}
	puts("");
}

双向链表

   插入

        双向链表的方式与单链表一样,只是多了往返指针,第一步依然是将插入节点的指针都连上,然后让后继节点的前驱指向新节点,最后前驱节点的后继指向新节点。只要新节点两个指针连接正确,后两步互换依然能找到节点2。

        双向指针的连接方式不唯一,节点1与新节点都能通过外部的指针找到,只要确保节点2不会丢失即可。

    删除

        删除与插入类似,依然时只要确保删除节点后的所有节点不会丢失,顺序有多种。

循环链表 

        循环链表是将链表最后一个节点的指针指向起始节点,该数据结构可以是单项指针也可以是是双向指针,因此其操作和单双指针类似。

数组模拟链式存储——单链表为例

        用数组模拟使用一个数据数组来存放数据,另一个数组来表示后继指针。这两个数组以同一个下标组成一个“结构体”。数据数组内部存放数据与data一样,而指针数组存放下一个节点的下标。因为数组是连续的,可以直接访问下标来获取数据,而结构体链表物理上是不连续的,需要指针记录地址访问。

        如图所示,通过访问头指针后将数据依此读取后是4,3,2,1;类似于结构体链表。这里采用从0开始使用数组,在《王道考研》中是不使用的,长度为n的线性表就是1~n+1。

         该代码未采用头结点,因此头插法和插入需要两个不同的函数来表示。同样的,根据模拟单链表,剩余两个数据结构也可以根据该代码进行扩展。

#include<iostream>
using namespace std;

const int N = 100;
int a[N],ne[N],index,head;

void init()
{
    head=-1,index=0;
}

void inserthead(int x)    //头插法
{
    a[index]=x;
    ne[index]=head;
    head=index;
    index++;
}

void remove(int k)    //删除第k个插入的数后面的数
{
    ne[k]=ne[ne[k]];
}

void insert(int k,int x)    //表示在第k个插入的数后面插入一个数x
{
    a[index]=x;
    ne[index]=ne[k];
    ne[k]=index;
    index++;
}

void print()
{
    for(int i=head;i!=-1;i=ne[i])   cout<<a[i]<<' ';
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值