【数据结构】单链表--基础


在实现单链表的基本功能后

单链表的几个基本问题:

1.比较顺序表和链表的优缺点,说说它们分别在什么场景下使用?

顺序表(物理位置相邻):优点:在一段内存中用数组连续存放,所以方便随机查找元素。

    缺点:动态开辟,容易造成内存浪费,需要一个元素,开辟过多。前面添加元素时,要逐个挪动后面的每个元素,较麻烦。

    场景:数据频繁查找修改,但很少添加。

单链表(物理位置不相邻):优点:方便开辟,用多少开辟多少,不会造成内存空间浪费。

    缺点:不方便直接查找。

    场景:频繁添加数据,但很少查询。

2.从尾到头打印单链表 :

可以运用递归的方法从尾到头打印。

从头节点向后递归,结束条件当节点为空

返回输出该节点的值。

void PrintTailToHead(ListNode *pList) //逆序打印 用递归
{
	if (pList == NULL)
		return;
	PrintTailToHead(pList->next);//子问题
	printf("%d->", pList->data);
}

3.删除一个无头单链表的非尾节点 :

  1.将后面节点的值赋给pos

  2.删除pos

void EraseNonTail(ListNode *pos) //无头删除指定非尾节点
{
	assert(pos);
	ListNode *cur = pos->next;
	//1.交换与后面的值
	DataType tmp = cur->data;
	cur->data = pos->data;
	pos->data = tmp;
	//2.删除后面的节点
	pos->next = cur->next;
	free(cur);
}

4.在无头单链表的一个节点前插入一个节点:

  1.在pos后面插入一节点

   2.将pos与后面的值交换

void InsertNonFront(ListNode *pos, DataType x) //在无头单链表的一个节点前插入一个节点
{
	assert(pos);
	//1.后面插入
	ListNode *tmp = BuyNode(x);
	tmp->next = pos->next;
	pos->next = tmp;
	//2.值交换
	DataType n = pos->data;
	pos->data = tmp->data;
	tmp->data = n;
}

5.单链表实现约瑟夫环 :

   已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依规律重复下去,直到圆桌周围的人全部出列。

  俩个循环 里循环走数k个,将第k个删除。外循环从删除后下一个再开始直到剩下一个节点。

ListNode* JoseohRing(ListNode *pList, int k)//约瑟夫环
{
	if (pList == NULL)
		return NULL;
	ListNode *cur = pList;
	while (cur->next != cur)
	{
		int count = k;
		while (--count) //走k-1次
		{
			cur = cur->next;
		}
		ListNode *next = cur->next;
		cur->data = next->data;
		cur->next = next->next;
		free(next);
	}
	return cur;
}

6.逆置/反转单链表 

ListNode* Reverse(ListNode *pList)//链表的逆置
{
	if ((pList == NULL) || (pList->next == NULL))
		return pList;
	else
	{
		ListNode *newHead = NULL;
		ListNode *cur = pList;
		while (cur)
		{
			ListNode *tmp = cur;
			cur = cur->next;
			tmp->next = newHead;
			newHead = tmp;
		}
		return newHead;
	}
}

7.单链表排序(冒泡排序|快速排序)

void BubbleSort(ListNode *pList)//冒泡排序
{
	if ((pList == NULL) || (pList->next == NULL))
		return;
	else
	{

		ListNode *tail = NULL;
		while (tail != pList->next)
		{
			int exchange = 0;
			ListNode *cur = pList;
			ListNode *next = cur->next;
			while (next != tail)
			{
				if (cur->data > next->data)
				{
					DataType tmp = cur->data;
					cur->data = next->data;
					next->data = tmp;
					exchange = 1;
				}
				next = next->next;
				cur = cur->next;
			}
			if (exchange == 0)
				return;
			tail = cur;
		}
	}
}

8.合并两个有序链表,合并后依然有序

ListNode* MergeList(ListNode *pList1, ListNode *pList2)//归并排序
{
	if (pList1 == NULL)
		return pList2;
	else if (pList2 == NULL)
		return pList1;
	else
	{
		//摘做头
		ListNode *list = NULL;
		if (pList2->data < pList1->data)
		{
			list = pList2;
			pList2 = pList2->next;
		}
		else
		{
			list = pList1;
			pList1 = pList1->next;
		}
		ListNode *tail = list;
		while (pList1 && pList2)
		{
			if (pList1->data < pList2->data)
			{
				tail->next = pList1;
				pList1 = pList1->next;
			}
			else
			{
				tail->next = pList2;
				pList2 = pList2->next;
			}
			tail = tail->next;
		}
		if (pList1)
			tail->next = pList1;
		else
			tail->next = pList2;
		return list;
	}
}

9.查找单链表的中间节点,要求只能遍历一次链表 

 定义 fast 与 slow fast每走2个slow走一个 fast走完全部,slow走了一半 为中节点

ListNode* FindMidNode(ListNode *pList)//查找单链表的中间节点
{
	ListNode *fast = pList;
	ListNode *slow = pList;
	ListNode *prev = pList;
	while ((fast)&&(fast->next))
	{
		fast = fast->next;
		fast = fast->next;
		prev = slow;
		slow = slow->next;
	}
	if (fast == NULL)
		slow = prev;
		return slow;
}

10.查找单链表的倒数第k个节点,要求只能遍历一次链表

   将fast先走k-1 然后与slow同时走 fast走动尾节点时,slow为倒数第k节点

//k<=链表个数
ListNode* FindTailkNode(ListNode *pList, DataType k)//查找单链表的倒数第k个节点
{
	if (pList == NULL)
		return NULL;
	ListNode *fast = pList;
	ListNode *slow = pList;
	while (--k)
	{
		fast = fast->next;
	}
	while (fast->next)
	{
		fast = fast->next;
		slow = slow->next;
	}
	return slow;
}








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值