链表的操作

链表的操作一直是困扰着本人,于是还是自己总结一番,针对性的加强训练。

下面是自己的一点点总结:

链表其实就是多个数据块通过指针连接起来,所谓的链表的操作,最核心也是最困难的就是调整指针的指向,每个结点只能也只有它能知道下一个结点的存储位置。改变链表的顺序就是不断的调整每个数据块的指针指向。因此,除非需要创建新的结点,会使用直接创建Node结点,否则一般都是创建Node类型的指针,通过指针不断的操作每个数据块中指针的指向,从而达到操作链表顺序的目的。


一些链表的复杂操作也基本上是由上面的操作组合构成。

链表主要分为:

单链表:单链表每个节点只包含一个后驱指针

双链表:双链表节点同时包含一个前驱指针和一个后驱指针

循环链表:循环链表的尾节点的后驱指向头节点

此外还需要注意的是:单链表还包括带头节点与不带头结点的两种。两者的差别不大,主要是为了代码操作的方便。

链表的创建:

单链表结点定义:

typedef struct Lnode
{ 
	ElemType data; /*数据域,保存结点的值 */
	struct Lnode *next; /*指针域*/
}LNode; /*结点的类型 */

创建单链表:

头插入法:从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止。即每次插入的结点都作为链表的第一个结点。生成的链表中结点的次序和输入的顺序相反。

尾插入法:将新结点插入到当前链表的表尾,使其成为当前链表的尾结点。生成的链表中结点的次序和输入的顺序相同。

  • 使用头插入法创建带头结点的单链表:


LNode* CreatLinkList()
{
	LNode *head = new LNode;//头结点包含实实在在的数据域,并不是一个空指针。
	head->next = NULL;
	for (int i=0;i<100;i++)
	{
		LNode* TempNode = new LNode;
		TempNode->data = i;
		TempNode->next = head->next;
		head->next = TempNode;
	}
	return head;
}

  • 使用头插入法创建不带头结点的单链表:

LNode* CreatLinkListNoHead()
{
	LNode* head = NULL;
	for (int i=0;i<100;i++)
	{
		LNode* TempNode = new LNode;
		TempNode->data = i;
		if(NULL == head)
		{
			head = TempNode;
			head->next = NULL;
		}
		else
		{
			TempNode->next = head;
			head = TempNode;
		}
	}
	return head;
}
//下面的方法也可以
LNode* CreatLinkListNoHead1()
{
	LNode* head = NULL;
	for (int i=0;i<100;i++)
	{
		LNode* TempNode = new LNode;
		TempNode->data = i;
		TempNode->next = head;
		head = TempNode;
	}
	return head;
}
  • 使用尾插法创建带头结点的单链表:

LNode* CreatLinkListTail()
{
	LNode* head = new LNode;
	LNode* CurNode = head;
	CurNode->next = NULL;
	for (int i=0;i<100;i++)
	{
		LNode* TempNode = new LNode;
		TempNode->data = i;
		TempNode->next = CurNode->next;
		CurNode->next = TempNode;
		CurNode = TempNode;
	}
	return head;
}

  • 使用尾插法创建不带头结点的单链表:

LNode* CreatLinkListTailNoHead()
{
	LNode* head = NULL;
	LNode* CurNode = head;
	for (int i=0;i<100;i++)
	{
		LNode* TempNode = new LNode;
		TempNode->data = i;
		if (NULL == head)
		{
			head = CurNode = TempNode;
			TempNode->next = NULL;
		}
		else
		{
			TempNode->next = CurNode->next;
			CurNode->next = TempNode;
			CurNode = TempNode;
		}
	}
	return head;
}
LNode* CreatLinkListTailNoHead()
{
	LNode* head = NULL;
	LNode* CurNode = head;
	for (int i=0;i<100;i++)
	{
		LNode* TempNode = new LNode;
		TempNode->data = i;
		TempNode->next = NULL;
		if (head == NULL)
		{
			head=CurNode = TempNode;
		}
		else
		{
			CurNode->next = TempNode;
			CurNode = TempNode;
		}
	}
	return head;
}

对于单链表,无论是哪种操作,只要涉及到钩链(或重新钩链),如果没有明确给出直接后继,钩链(或重新钩链)的次序必须是“先右后左”。

当一个指针为NULL时,千万不要使用此指针进行操作,一定要进行判断,例如

LNode* head = NULL;
	head->next = NULL;
就是大错特错的。

此外通过上述操作可以发现,不带头结点的时候需要对头结点为空的情形进行特殊处理。带有头结点的单链表只不过浪费了一个结点的数据存储域,(不一定是浪费,这个数据域可用来存储一些需要的数据,例如单链表的长度等)在操作的时候不需要判断头结点为空的情形,代码更加简洁有效。头结点的数据域可以不存储任何信息,头结点的指针域存储指向第一个结点的指针(即第一个元素结点的存储位置)。头结点的作用是使所有链表(包括空表)的头指针非空,并使对单链表的插入、删除操作不需要区分是否为空表或是否在第一个位置进行,从而与其他位置的插入、删除操作一致。


单链表的插入操作:

  • 带有头结点的单链表插入操作:

bool InsertValue(LNode* head,int Index,int KeyValue)
{
	if (head == NULL || Index < 0)
	{
		return false;
	}
	
	LNode *CurNode = head;
	int i = -1;
	while (CurNode!=NULL && i<Index-1)
	{
		CurNode= CurNode->next;
		i++;
	}
	if (i==Index-1)
	{
		LNode* TempNode  = new LNode;
		TempNode->data = KeyValue;
		TempNode->next = CurNode->next;
		CurNode->next = TempNode;
		return true;
	}
}

插入时一定要考察参数的有效性,其中就包括Index的合法性等,同时插入操作需要问清楚面试官的要求,比如插入位置为0时是不是插入第一个结点。这牵涉到计数器i的初始值。

  • 不带头结点的单链表的插入操作:

bool InsertValueNohead(LNode*& head,int Index,int Key)
{
	if (head ==NULL || Index <0)
	{
		return false;
	}
	if (Index == 0)
	{
		LNode* TempNode = new LNode;
		TempNode->data = Key;
		TempNode->next = head;
		head = TempNode;
		return true;
	}
	LNode * CurNode = head;
	int i = 0;
	while (CurNode!=NULL && i<Index-1)
	{
		CurNode= CurNode->next;
		i++;
	}
	if (i==Index-1)
	{
		LNode* TempNode  = new LNode;
		TempNode->data = Key;
		TempNode->next = CurNode->next;
		CurNode->next = TempNode;
		return true;
	}
	return false;
	
}

上述代码中需要注意的是:

  1. 需要判断插入位置是否是头结点,如果是头结点的话需要单独操作,这就是与带头结点单链表的区别,因为带头结点的单链表的第一个结点始终都不会改变,而不带头结点的单链表的第一个结点有可能被改变。
  2. 头结点需要改变的时候,由于返回值是bool类型,因此在传值的时候使用了引用类型。否则会没有效果。

单链表的删除操作: 

单链表的删除操作可以分为:按序号删除结点,按值删除结点

按序号删除:

带头结点的按序号删除:

bool DeleteNode(LNode* head,int Index)
{
	if (head == NULL || Index <= 0)
	{
		return false;
	}
	LNode * CurNode = head;
	int i=0;
	while (CurNode!=NULL && i<Index-1)
	{
		CurNode = CurNode->next;
		i++;
	}
	if (i != Index-1)
	{
		return false;
	}
	LNode* p = CurNode->next;
	CurNode->next = p->next;
	delete p;
	return true;
}

不带头结点的单链表按序号删除:

bool DeleteNodeNoHead(LNode*& head,int Index)
{
	if (head==NULL || Index<=0)
	{
		return false;
	}
	if (Index == 1)
	{
		LNode* Node = head;
		head = head->next;
		delete Node;
	}
	else
	{
		LNode * CurNode = head;
		int i=1;
		while (CurNode!=NULL && i<Index-1)
		{
			CurNode = CurNode->next;
			i++;
		}
		if (i != Index-1)
		{
			return false;
		}
		LNode* p = CurNode->next;
		CurNode->next = p->next;
		delete p;
		return true;
	}
}

带头结点按值删除操作:

bool DeleteNodeKey(LNode* head, int key)
{
	if (head == NULL)
	{
		return false;
	}
	LNode* CurNode  = head;
	LNode* TempNode = NULL;
	while ( CurNode->next!=NULL && CurNode->next->data != key)
	{
		CurNode = CurNode->next;
	}
	if (CurNode->next->data == key)
	{
		TempNode = CurNode->next;
		CurNode->next= TempNode->next;
		delete TempNode;
		return true;
	}
	return false;
}
不带头结点的按值删除操作

bool DeleteNodeKeyNoHead(LNode*& head,int key)
{
	if (head == NULL)
	{
		return false;
	}
	if (head->data == key)
	{
		LNode* Node = head;
		head = Node->next;
		delete Node;
		return true;
	}
	else
	{
		LNode* CurNode  = head;
		LNode* TempNode = NULL;
		while ( CurNode->next!=NULL && CurNode->next->data != key)
		{
			CurNode = CurNode->next;
		}
		if (CurNode->next->data == key)
		{
			TempNode = CurNode->next;
			CurNode->next= TempNode->next;
			delete TempNode;
			return true;
		}
		return false;
	}
	
}
关于链表的插入或者删除操作,都要首先找到要删除结点的前一个结点。因为每个结点所包含的的链接信息只是它的下一个结点,其余的信息一概不知。












  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python链表操作包括创建链表、合并链表、删除链表的元素以及翻转链表的一段位置的元素。首先,我们可以通过创建一个链表节点类和链表操作类来实现这些操作链表节点类定义了节点的值和指向下一个节点的指针。链表操作类包含了创建链表、浏览链表等方法。 创建链表操作通过传入一个列表来创建链表。每个列表元素都会被转换为一个节点,并通过指针连接起来。浏览链表操作可以将链表的元素遍历并返回一个列表。 除此之外,还可以进行一些其他的链表操作,如统计链表某个元素出现的次数。可以通过调用列表的count()方法来实现。 还可以删除链表倒数第n个元素并返回头结点。这个操作可以通过一次扫描实现。首先,将链表的每个元素指向一个列表的每个元素。然后,根据n的值来删除链表的对应节点。 总之,Python提供了丰富的链表操作方法,可以根据需求进行选择和使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [链表操作基础(python)](https://blog.csdn.net/beautiful77moon/article/details/120443389)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [python链表所有操作详解](https://blog.csdn.net/starmoth/article/details/88323536)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值