大魔王程序员#01#数据结构-单链表

14 篇文章 0 订阅

目录

1.逆置单链表

2.反转单链表

3.合并两个有序的单链表

         4.判断单链表是否有环?环的入口点?环的长度?环的入口节点

5.判断两个单链表是否相交?交点?

6.O(1)时间删除单链表的一个节点

7.最快时间内找到单链表倒数第K个节点

8.最快时间内删除单链表倒数第K个节点

参考代码


1.逆置单链表

思路:已知单链表后,根据头插法的方式,将头结点后的第一个节点的next置为空,然后将后面结点一次挂在它的前面

如图所示:

代码实现:

void Reverse2(List plist)//头插法逆置
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return ;
	}
	Node *cur = plist->next;
	plist->next = NULL;
	while(cur != NULL)
	{
		Node *curNext = cur->next;
		cur->next = plist->next;
		plist->next = cur;
		cur = curNext;
	}
}

2.反转单链表

思路:依次将每个节点取出,从后往前排列.设置一个end中间节点,开始时将它的next置为NULL,然后将它的地址赋给它的前一个节点的next.

注:将它理解为取出第一个节点(即头结点,将它的next置为NULL,作为新链表最后的节点,然后取原链表下一个节点,将它的next附为新链表最后一个节点的地址)

如图所示:

代码实现:

Node *Fun(List plist)// 单链表反转
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return false;
	}
	Node *cur = plist;
	Node *end = NULL;
	while(cur != NULL)
	{
		Node *tmp = cur;
		cur = cur->next;
		tmp->next = end;
		end = tmp;
	}
	return end;
}

问:那么单链表的逆置和反转有什么区别呢?


3.合并两个有序的单链表

思路:比较两个链表值的大小,将小的排在前面;由于最开始比较的是头结点的值,所以最后显示时要多往后走一步.

注意:要注意链表是否为空的情况,当第一个链表为空链表,也就是它的头结点是一个空指针,那么把它和第二个链表合并,显然合并的结果就是第二个链表;同理,第二个链表的头结点是空指针的时候,把它和第一个链表合并,结果就是第一个链表.如果两个链表都是空链表,则合并结果是得到一个空链表.

代码实现:

Node *Merge(List plist1,List plist2)
{
	if(plist1 == NULL)
	{
		return plist2;
	}
	else if(plist2 == NULL)
	{
		return plist1;
	}
	Node *pMergeHead = NULL;
	if(plist1->data < plist2->data)
	{
		pMergeHead = plist1;
		pMergeHead->next = Merge(plist1->next,plist2);
	}
	else
	{
		pMergeHead = plist2;
		pMergeHead->next = Merge(plist1,plist2->next);
	}
	return pMergeHead;
}

void Show1(List plist)
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return;
	}
	Node *cur = plist->next->next;
	while(cur->next != NULL)
	{
		printf("%d ",cur->data);
		cur = cur->next;
	}
	printf("%d \n",cur->data);
}

int main()
{
	Node sq;
	InitList(&sq);
	for(int i = 0;i < 9;i++)
	{
		Insert_tail(&sq,i+3);
	}
	Show(&sq);
	printf("============\n");
	//合并两个有序单链表
	Node sq2;
	InitList(&sq2);
	for(int i = 0;i < 4;i++)
	{
		Insert_tail(&sq2,i+10);
	}
	Show(&sq2);
	printf("===============\n");
	Node *sq3 = NULL;
	sq3 = Merge(&sq,&sq2);
	Show1(sq3);
	return 0;
}


4.判断单链表是否有环?环的入口点?环的长度?

在网上看到一哥们儿写的解释特别好,所以将他的讲解和图借来用用,代码是我自己写的,特此声明.

判断是否有环

如果链表有环,那么在遍历链表时则会陷入死循环,利用这个特征,我们可以设计这样的算法。

如果fast指针在遍历过程中,遍历到了NULL节点说明链表没有环。

否则当slow指针和falst指针相同,则说明环有节点。

 

  • 使用一个slow指针,一个fast指针。
  • slow指针一次往后遍历以1个节点,fast指针一次往后遍历2个节点,一直做这样的操作。

环的入口节点

 

假定链表头到环入口的距离是len,环入口到slow和fast交汇点的距离为x,环的长度为R。slow和fast第一次交汇时,设slow走的长度为:d = len + x,而fast走的长度为:2d = len + nR + x,(n >= 1),从而我们可以得知:2len + 2x = len + nR + x,即len = nR - x,(n >= 1),于是我们可以得到这样的算法。

使用一个cur指针指向链表头节点,一个inter指针指向第一次的交汇点。

cur指针和inter指针一起往后遍历.

cur指针和inter指针相等时,cur和inter指针指向的就是环的入口节点。

代码实现:

 
void CreatLoop(List plist)
{
	assert(plist != NULL);
	Node *cur = plist;
	Node *cur1 = plist;
	while(cur->next != NULL)
	{
		cur = cur->next;
	}
	cur->next = cur1->next->next->next->next;
}

Node *Fun2(List plist,int *count)//判断单链表是否有环?环的入口点?环的长度?
{
	assert(plist != NULL &&count != NULL);
	if(IsEmpty(plist))
	{
		return NULL;
	}
	Node *pb = plist->next;
	Node *pe = plist->next;
	Node *cur = plist->next;
	Node *inter = plist->next;
	while(pb->next != NULL && pe->next != NULL )
	{
		pb = pb->next;//慢指针走一步
		pe = pe->next->next; //快指针走两步
		if(pb == pe)
		{
			inter = pb;//len = nR-x;    len :第一个节点到入口点的距离  R:环长  x:入口点到交互点的距离
			printf("it have loop\n");
			break;
		}
	}
	
	while(cur != inter)
	{
		cur = cur->next;
		inter = inter->next;
	}
	int foot = 1;
	cur = cur->next;
	while(inter != cur)
	{
		foot++;
		cur = cur->next;
	}
	*count = foot;
	return inter;
}

5.判断两个单链表是否相交?交点?

问:在解释之前,先提出一个问题,两个单链表相交,他是什么形状的?是X型?还是Y型?

答:那当然是Y型了,不然你以为呢.

思路:要想知道两个单链表是否相交,最简单的方法就是看他们最后一个节点的地址是否相同.因为相交之后的节点都是一样的.那麻烦的是,如何去找交点.大致思路就是,假设两个单链表的长短不一,然后去计算他们相差的节点数X,然后让长的链表往后走X步,此时两个单链表到最后的长度是一样的,这样就可以边走,边进行判断,看他们是否一样.

如图所示:

代码实现:

void CreateCut(List plist,List plist1)//节点相交
{
	plist->next->next = plist1->next->next->next;
}

bool IsCut(List plist1,List plist2)//判断是否相交,Y字形
{
	assert(plist1 != NULL && plist2 != NULL);
	if(IsEmpty(plist1) || IsEmpty(plist2))
	{
		return false;
	}
	int len1 = GetLength(plist1);
	int len2 = GetLength(plist2);
	Node *plong = plist1;
	Node *pshort = plist2;
	int len = len1-len2;
	if(len < 0) 
	{
		plong = plist2;
		pshort = plist1;
		len = len2-len1;
	}
	//plong肯定指向长的   pshort 
	//len > 0
	for(int i = 0;i < len;i++)
	{
		plong = plong->next;
	}
	while(plong != NULL && pshort != NULL && plong != pshort)
	{
		plong = plong->next;
		pshort = pshort->next;
	}
	if(plong == pshort && plong != NULL)
	{
		return true;
	}
	return false;
}

Node* IsCutNode(List plist1,List plist2)
{
	int len1 = GetLength(plist1);
	int len2 = GetLength(plist2);
	Node *plong = plist1;
	Node *pshort = plist2;
	int len = len1-len2;
	if(len < 0) 
	{
		plong = plist2;
		pshort = plist1;
		len = len2-len1;
	}
	//plong肯定指向长的   pshort 
	//len > 0
	for(int i = 0;i < len;i++)
	{
		plong = plong->next;
	}
	while(plong != NULL && pshort != NULL && plong != pshort)
	{
		plong = plong->next;
		pshort = pshort->next;
	}
	if(plong == pshort && plong != NULL)
	{
		return plong;
	}
	return NULL;
}

6.O(1)时间删除单链表的一个节点

思路:如果是循环删除,那时间复杂度为O(n),而这道题是让在O(1)的时间内完成,显然说的是平均时间.为什么呢?因为如果是要删除最后一个节点,就必须知道他的前一个节点,不然怎么删除.这就意味着,此时需要遍历一遍单链表.如果是非最后一个节点要被删除,直接用后一个节点的数据覆盖它即可.所以这道题说的是平均时间复杂度O(1).

代码实现:

void DeleteNode(List plist,Node *pDel)//O(1)时间删除单链表的一个节点  pDel==>要删除的节点
{
	assert(plist != NULL && pDel != NULL);
	if(IsEmpty(plist))
	{
		return ;
	}
	Node *cur1 = plist;
	if(pDel->next != NULL)
	{
		Node *pDelNext = pDel->next;
		pDel->data = pDelNext->data;
		pDel->next = pDelNext->next;
		free(pDelNext);
	}
	else
	{
		Node *pCur = plist;
		while(pCur->next != pDel)
		{
			pCur = pCur->next;
		}
		pCur->next = NULL;
		free(pDel);
	}
}

7.最快时间内找到单链表倒数第K个节点

思路:用最快时间找到第k个节点,显然使用循环遍历到所找节点这样的方法不是最快的.那该如何解答?应该设置两个指针,让他们相差k-1步,当靠后的指针走到链尾,前面的指针正好在所找节点上

如图所示:

 代码实现:

Node *LastK(List plist,int k)//最快时间内找到单链表倒数第K个节点
{
	assert(plist != NULL);
	if(k < 0 || k > GetLength(plist) || IsEmpty(plist))
	{
		return NULL;
	}
	Node *cur1 = plist;
	Node *cur2 = plist;
	while(k-1 > 0)
	{
		if(cur1->next != NULL)
		{
			cur1 = cur1->next;
			--k;
		}
		else
		{
			return NULL;
		}
	}
	while(cur1->next != NULL)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	return cur2;
}

8.最快时间内删除单链表倒数第K个节点

思路:和上一个算法的思想一样,只是多加了一步找到后删除

代码实现:

void DeleteLastK(List plist,int k)//最快时间内删除单链表倒数第K个节点
{
	assert(plist != NULL);
	if(k < 0 || k > GetLength(plist) || IsEmpty(plist))
	{
		return ;
	}
	Node *cur1 = plist;
	Node *cur2 = plist;
	while(k > 0)
	{
		if(cur1->next != NULL)
		{
			cur1 = cur1->next;
			--k;
		}
		else
		{
			return;
		}
	}
	while(cur1->next != NULL)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	Node *tmp = cur2->next;
	cur2->next = tmp->next ;
	free(tmp);
	tmp = NULL;
}

以下是完整代码,仅供参考

//linklist.cpp
#include"linklist.h"
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>


void InitList(List plist)//初始化单链表
{
	assert(plist != NULL);
	plist->next = NULL;
}

static Node *GetNode(int val)
{
	Node* pGet = (Node*)malloc(sizeof(Node));
	assert(pGet != NULL);
	pGet->data = val;
	pGet->next = NULL;
	return pGet;
}

bool Insert_head(List plist,int val)//头插法
{
	assert(plist != NULL);
	Node *pGet = GetNode(val);
	pGet->next = plist->next;
	plist->next = pGet;
	return true;
}

bool Insert_tail(List plist,int val)//尾插法
{
	assert(plist != NULL);
	Node *cur = plist;//指向头结点处     依赖前驱信息的
	while(cur->next != NULL)
	{
		cur = cur->next;
	}
	Node *pGet = GetNode(val);
	cur->next = pGet;
	return true;
}

bool Insert_pos(List plist,int pos,int val)//pos 位置插入
{
	assert(plist != NULL);
	if(pos < 0 || pos > GetLength(plist))
	{
		return false;
	}
	Node *cur = plist;
	for(int i = 0;i <= pos-1;i++)
	{
		cur = cur->next;
	}
	Node *pGet = GetNode(val);
	pGet->next = cur->next;
	cur->next = pGet;
	return true;
}

Node *Search_pre(List plist,int key)//查找 key 的前驱
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return NULL;
	}
	Node *cur = plist;
	while(cur->next != NULL)
	{
		if(cur->next->data != key)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

bool Delete(List plist,int key)//删除 key 这个结点
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return false;
	}
	Node *cur = plist;
	while(cur->next != NULL)
	{
		if(cur->next->data == key)
		{
			Node *p = cur->next;
			cur->next = p->next;
			free(p);
			p = NULL;
			return true;
		}
		cur = cur->next;
	}
	return false;
}

bool IsEmpty(List plist)//是否为空
{
	if(plist == NULL || plist->next == NULL)
	{
		return true;
	}
	return false;
}

void Destroy(List plist)//摧毁函数(如果有动态开辟内存)
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return;
	}
	Node *cur = plist;
	while(cur->next != NULL)
	{
		Node *p = cur->next;
		cur->next = p->next;
		free(p);
		p = NULL;
	}
	free(cur->next);
	plist->next = NULL;
	return ;
}

int GetLength(List plist)//得到单链表的长度
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return 0;
	}
	int count = 0;
	Node *pCur = plist->next;
	while(pCur != NULL)
	{
		count++;
		pCur = pCur->next;
	}
	return count;
}

void Show(List plist)//打印单链表
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return;
	}
	Node *cur = plist;
	while(cur->next != NULL)
	{
		printf("%d  %d\n",cur->data,cur->next);
		cur = cur->next;
	}
	printf("%d  %d\n",cur->data,cur->next);
}

Node *Fun(List plist)//带头结点逆置 == 反转
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return false;
	}
	Node *cur = plist;
	Node *end = NULL;
	while(cur != NULL)
	{
		Node *tmp = cur;
		cur = cur->next;
		tmp->next = end;
		end = tmp;
	}
	return end;
}

bool Fun1(List plist,List plist1)//合并两个有序单链表
{
	assert(plist != NULL && plist1 != NULL);
	if(IsEmpty(plist) || IsEmpty(plist1))
	{
		return false;
	}
	Node *cur1 = plist;
	Node *cur2 = plist1->next;
	while(cur1->next != NULL)
	{
		cur1 = cur1->next;
	}
	while(cur2 != NULL)
	{
		cur1->next = cur2;
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	cur1->next = cur2;
	return true;
}

void CreatLoop(List plist)
{
	assert(plist != NULL);
	Node *cur = plist;
	Node *cur1 = plist;
	while(cur->next != NULL)
	{
		cur = cur->next;
	}
	cur->next = cur1->next->next->next->next;
}

Node *Fun2(List plist,int *count)//判断单链表是否有环?环的入口点?环的长度?
{
	assert(plist != NULL &&count != NULL);
	if(IsEmpty(plist))
	{
		return NULL;
	}
	Node *pb = plist->next;
	Node *pe = plist->next;
	Node *cur = plist->next;
	Node *inter = plist->next;
	while(pb->next != NULL && pe->next != NULL )
	{
		pb = pb->next;//慢指针走一步
		pe = pe->next->next; //快指针走两步
		if(pb == pe)
		{
			inter = pb;//len = nR-x;    len :第一个节点到入口点的距离  R:环长  x:入口点到交互点的距离
			printf("it have loop\n");
			break;
		}
	}
	
	while(cur != inter)
	{
		cur = cur->next;
		inter = inter->next;
	}
	int foot = 1;
	cur = cur->next;
	while(inter != cur)
	{
		foot++;
		cur = cur->next;
	}
	*count = foot;
	return inter;
}

void CreateCut(List plist,List plist1)//节点相交
{
	plist->next->next = plist1->next->next->next;
}

bool IsCut(List plist1,List plist2)//判断是否相交,Y字形
{
	assert(plist1 != NULL && plist2 != NULL);
	if(IsEmpty(plist1) || IsEmpty(plist2))
	{
		return false;
	}
	int len1 = GetLength(plist1);
	int len2 = GetLength(plist2);
	Node *plong = plist1;
	Node *pshort = plist2;
	int len = len1-len2;
	if(len < 0) 
	{
		plong = plist2;
		pshort = plist1;
		len = len2-len1;
	}
	//plong肯定指向长的   pshort 
	//len > 0
	for(int i = 0;i < len;i++)
	{
		plong = plong->next;
	}
	while(plong != NULL && pshort != NULL && plong != pshort)
	{
		plong = plong->next;
		pshort = pshort->next;
	}
	if(plong == pshort && plong != NULL)
	{
		return true;
	}
	return false;
}


Node *Reverse1(List plist)//单链表反转
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return NULL;
	}
	Node *cur = plist;
	Node *prev = NULL;
	Node *reverHead = NULL;
	while(cur != NULL)
	{
		Node *curNext = cur->next;
		if(curNext == NULL)
		{
			reverHead = cur;
		}
		cur->next = prev;//NULL
		prev = cur;
		cur = curNext;
	}
	return reverHead;
}
void Show2(Node *reverHead)
{
	Node *p = reverHead;
	while(p->next != NULL)
	{
		printf("%d ",p->data);
		p = p->next;
	}
	printf("\n");
}

void Reverse2(List plist)//头插法逆置
{
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return ;
	}
	Node *cur = plist->next;
	plist->next = NULL;
	while(cur != NULL)
	{
		Node *curNext = cur->next;
		cur->next = plist->next;
		plist->next = cur;
		cur = curNext;
	}
}

/*
合并两个有序的单链表
判断单链表是否有环?环的入口点?环的长度?
判断两个单链表是否相交?交点?
*/
Node *LastK(List plist,int k)//最快时间内找到单链表倒数第K个节点
{
	assert(plist != NULL);
	if(k < 0 || k > GetLength(plist) || IsEmpty(plist))
	{
		return NULL;
	}
	Node *cur1 = plist;
	Node *cur2 = plist;
	while(k-1 > 0)
	{
		if(cur1->next != NULL)
		{
			cur1 = cur1->next;
			--k;
		}
		else
		{
			return NULL;
		}
	}
	while(cur1->next != NULL)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	return cur2;
}

void DeleteLastK(List plist,int k)//最快时间内删除单链表倒数第K个节点
{
	assert(plist != NULL);
	if(k < 0 || k > GetLength(plist) || IsEmpty(plist))
	{
		return ;
	}
	Node *cur1 = plist;
	Node *cur2 = plist;
	while(k > 0)
	{
		if(cur1->next != NULL)
		{
			cur1 = cur1->next;
			--k;
		}
		else
		{
			return;
		}
	}
	while(cur1->next != NULL)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	Node *tmp = cur2->next;
	cur2->next = tmp->next ;
	free(tmp);
	tmp = NULL;
}

void DeleteNode(List plist,Node *pDel)//O(1)时间删除单链表的一个节点  pDel==>要删除的节点
{
	assert(plist != NULL && pDel != NULL);
	if(IsEmpty(plist))
	{
		return ;
	}
	Node *cur1 = plist;
	if(pDel->next != NULL)
	{
		Node *pDelNext = pDel->next;
		pDel->data = pDelNext->data;
		pDel->next = pDelNext->next;
		free(pDelNext);
	}
	else
	{
		Node *pCur = plist;
		while(pCur->next != pDel)
		{
			pCur = pCur->next;
		}
		pCur->next = NULL;
		free(pDel);
	}
}

 

//linklist.h
#pragma once//保证头文件只被编译一次


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

void InitList(List plist);//初始化单链表

bool Insert_head(List plist,int val);//头插法

bool Insert_tail(List plist,int val);//尾插法

bool Insert_pos(List plist,int pos,int val);//pos 位置插入

Node *Search_pre(List plist,int key);//查找 key 的前驱

bool Delete(List plist,int key);//删除 key 这个结点

bool IsEmpty(List plist);//是否为空

void Destroy(List plist);//摧毁函数(如果有动态开辟内存)

int GetLength(List plist);//得到单链表的长度

void Show(List plist);//打印单链表

Node *Fun(List plist);//带头结点逆置

bool Fun1(List plist,List plist1);//合并两个有序单链表

void CreatLoop(List plist);

Node *Fun2(List plist,int *count);//判断单链表是否有环?环的入口点?环的长度?

Node *Reverse1(List plist);//单链表反转

void Show2(Node *reverHead);//反转之后的打印

void Reverse2(List plist);//头插法逆置

Node *LastK(List plist,int k);//最快时间内找到单链表倒数第K个节点

void DeleteNode(List plist,Node *del);//O(1)时间删除单链表的一个节点  del==>要删除的节点

bool IsCut(List plist1,List plist2);//判断是否相交,Y字形
//源.cpp
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include"linklist.h"
//#include<vld.h>//测内存泄漏


int main()
{
	Node sq;
	InitList(&sq);
	for(int i = 0;i < 9;i++)
	{
		//Insert_head(&sq,i+3);//头插法逆置
		Insert_tail(&sq,i+3);
	}
	Show(&sq);
	printf("============\n");
	
	int count = 0; 
	Node *inter = NULL;
	CreatLoop(&sq);
	inter = Fun2(&sq,&count);
	printf("环入口为:%d %d\n",inter->data,inter->next);
	printf("环长为:%d\n",count);


        /*Node head;//头结点
	InitList(&head);
	for(int i = 0; i < 10;i++)
	{
		Insert_head(&head,i);
	}
	Show(&head);
	printf("=======\n");
	Node head2;//头结点
	InitList(&head2);
	for(int i = 10; i < 30;i++)
	{
		Insert_head(&head2,i);
	}
	Show(&head2);
	printf("=======\n");
	CreateCut(&head,&head2); 两个链表相交
	if(IsCut(&head,&head2))
	{
		Node *p = IsCutNode(&head,&head2);
		printf("is  cut ! %d\n",p->data);
	}
	else
	{
		printf("is not cut!\n");
	}*/


	//Reverse2(&sq);//头插法逆置
	//Show(&sq);

	/*int len = GetLength(&sq);
	printf("%d\n",len);*/

	/*Insert_pos(&sq,2,99);
	Show(&sq);
	Delete(&sq,99);
	Show(&sq);*/

	//带头结点的逆置  == 反转
	/*Node *sq1= Fun(&sq);
	Show(sq1);*/

	//合并两个有序单链表
	/*Node sq2;
	InitList(&sq2);
	for(int i = 0;i < 4;i++)
	{
		Insert_tail(&sq2,i+10);
	}
	Show(&sq2);
	printf("===============\n");
	Fun1(&sq,&sq2);
	Show(&sq);*/



	/*Destroy(&sq);
	printf("===============\n");
	Show(&sq);*/

	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值