Data Structures (二) 链表 附带链表常见面试题

一、链表

一种链式存储的线性表,用一组地址任意的存储单元存储线性表的数据,称一个存储单元为一个结点。

链表的分类:

1、单链表;2、双向链表;3、带头指针链表;4、不带头指针链表;5、循环链表;6、非循环链表

二、单链表:用一组任意的存储单元存放,位置可以使连续的,不连续的,零散的。

结点包含两个域------数据域、指针域。数据域存放数据的值、指针域存放该元素的直接后继的地址,由于该结构只有一个指针域,所以叫做单链表。

单链表的头指针标志着点链表的开始,头指针指向第一个节点的地址,最后一个节点没有直接后继,所以最后一个节点的指针域为空。

#pragma once

typedef size_t DataType;


//单链表的结构定义
typedef struct SListNode  //节点的结构类型
{
	struct SListNode *_next;
	DataType _data;
}SListNode;  //linklist 为单链表结构的指针类型

//创建结点
SListNode* CreateSLitNode(DataType x)
{
	SListNode* node = (SListNode*)malloc(sizeof(SListNode));
	(node)->_data = x;
	(node)->_next = NULL;
	return node;
}

//尾插
void SListInsertBack(SListNode** ppHead, DataType x)
{
	if (*ppHead == NULL)
	{
		*ppHead = CreateSLitNode(x);
	}
	else
	{
		SListNode * cur = *ppHead;
		while (cur->_next != NULL)
		{
			cur = cur->_next;
		}
		cur->_next = CreateSLitNode(x);
	}
}

//头插
void SListInsertFront(SListNode** ppHead, DataType x)
{
	if (*ppHead == NULL)
	{
		*ppHead = CreateSLitNode(x);
	}
	else
	{
		SListNode* newNode = CreateSLitNode(x);
		newNode->_next = *ppHead;
		*ppHead = newNode;
	}
}

//尾删
void SListPopBack(SListNode** ppHead)
{
	if (*ppHead == NULL)
	{
		return;
	}
	else if ((*ppHead)->_next == NULL)
	{
		free(*ppHead);
		*ppHead = NULL;
	}
	else
	{
		SListNode* prev = NULL, *cur = *ppHead;
		//SListNode* cur = *ppHead;
		while (cur->_next)
		{
			prev = cur;
			cur = cur->_next;
		}
		free(cur);
		prev->_next = NULL;
	}
}

//头删
void SListPopFront(SListNode** ppHead)
{
	assert(*ppHead);
	SListNode* next = (*ppHead)->_next;
	free(*ppHead);
	*ppHead = next;

}

SListNode* FindSListNode(SListNode* head, DataType x)
{
	assert(head);
	SListNode * cur;
	cur = head->_next;
	while (cur != NULL)
		if (cur->_data != x)
			cur = cur->_next;
		else
			break;
	return cur;
}

//打印单链表
void printSList(SListNode* pHead)
{
	SListNode* cur = pHead;
	while (cur != NULL)
	{
		cout << cur->_data << "->";
		cur = cur->_next;
	}
	cout << "NULL" << endl;
}

//释放单链表
void DestorySList(SListNode** ppHead)
{
	SListNode* cur = *ppHead;
	while (cur)
	{
		SListNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	*ppHead = NULL;
}

//从尾到头打印
void printTailToHead(SListNode* head)
{
	//正常写法
	if (head == NULL)
	{
		return;
	}
	else
	{
		SListNode* tail = NULL;
		while (head != tail)
		{
			SListNode* cur = head;
			while (cur->_next != tail)
			{
				cur = cur->_next;
			}
			cout << cur->_data << "->";
			tail = cur;
		}
		cout << "NULL" << endl;
	}

	//递归写法
	/*if (head == NULL)
	{
		return;
	}
	printTailToHead(head->_next);
	cout << head->_data << " ";*/
}



//删除无头单链表的非尾结点(不能遍历链表)
void without_Head_DelSList(SListNode* pos)
{
	assert(pos&&pos->_next);
	SListNode* next;
	next = pos->_next;
	pos->_data = next->_data;
	pos->_next = next->_next;
	free(next);
}



//在无头单链表的一个节点前插入一个结点(不能遍历链表)
void without_Head_Insert(SListNode* pos, DataType x)
{
	SListNode *node = CreateSLitNode(pos->_data);
	pos->_data = x;
	node->_next = pos->_next;
	pos->_next = node;
}

//单链表实现约瑟夫环
SListNode* JosephCycle(SListNode* head, int k)
{
	assert(head);
	//构成环
	SListNode* tail = head;
	while (tail->_next)
	{
		tail = tail->_next;
	}
	tail->_next = head;

	SListNode* cur = head;
	while (cur->_next != cur)
	{
		int count = k;
		while (--count)
		{
			cur = cur->_next;
		}
		without_Head_DelSList(cur);
	}
	return cur;
}


//逆置单链表
void ReverseSList(SListNode ** head)
{

	//原地逆置法
	if (*head == NULL || (*head)->_next == NULL)
	{
		return;
	}
	SListNode* p1 = *head;
	SListNode* p2 = p1->_next;
	SListNode* p3 = p2->_next;

	while (p2)
	{
		p2->_next = p1;
		p1 = p2;
		p2 = p3;
		if (p3)
		{
			p3 = p3->_next;
		}
	}
	(*head)->_next = NULL;
	*head = p1;

	//头插法
	/*SListNode* newHead = NULL;
	SListNode* cur = *head;
	while (cur)
	{
		SListNode* tmp = cur;
		cur = cur->_next;
		tmp->_next = newHead;
		newHead = tmp;
	}
	*head = newHead;*/
}

//单链表实现冒泡排序
void BubbleSortList(SListNode* head)
{
	if (head == NULL || head->_next == NULL)
	{
		return;
	}
	SListNode* tail = NULL;
	SListNode* cur;
	SListNode* next;
	while (tail != head->_next)
	{
		cur = head;
		next = cur->_next;
		while (next != tail)
		{
			if (cur->_data > next->_data)
			{
				DataType tmp = cur->_data;
				cur->_data = next->_data;
				next->_data = tmp;
			}
			cur = cur->_next;
			next = next->_next;
		}
		tail = cur;
	}
}


//合并两个有序链表
SListNode* MergeList(SListNode* list1, SListNode* list2)
{
	/*SListNode* newList = NULL;
	if (list1 == NULL)
		return list2;
	if (list2 == NULL)
		return list1;
	if (list1 == NULL&&list2 == NULL)
		return NULL;

	if (list1->_data < list2->_data)
	{
		newList = list1;
		list1 = list1->_next;
	}
	else
	{
		newList = list2;
		list2 = list2->_next;
	}
	SListNode* cur = newList;
	while (list1 && list2)
	{
		if (list1->_data < list2->_data)
		{
			cur->_next = list1;
			list1 = list1->_next;
		}
		else
		{
			cur->_next = list2;
			list2 = list2->_next;
		}
		cur = cur->_next;
	}
	if (list1)
		cur->_next = list1;
	if (list2)
		cur->_next = list2;
	return newList;*/


	//递归写法
	SListNode* newList = NULL;
	if (list1 == NULL)
		return list2;
	if (list2 == NULL)
		return list1;
	if (list1 == NULL&&list2 == NULL)
		return NULL;
	if (list1->_data < list2->_data)
	{
		newList = list1;
		newList->_next = MergeList(list1->_next, list2);
	}
	else
	{
		newList = list2;
		newList->_next = MergeList(list1, list2->_next);
	}
	return newList;
}

//查找单链表的中间节点,要求只能遍历一遍链表
SListNode* FindMiddle(SListNode* head)
{
	if (head == NULL)
	{
		return NULL;
	}

	SListNode* slow = head;
	SListNode* fast = head;
	while (fast->_next)
	{
		fast = fast->_next;
		slow = slow->_next;
		if (fast->_next)
		{
			fast = fast->_next;
		}
	}
	return slow;
}

//查找单链表的倒数第k个结点,要求只能遍历一遍链表
SListNode* Find_K_Backward(SListNode* head, DataType k)
{
	//思路:两个同步指针,第一个走k-1,然后两个同时向后,快指针走完后,倒数第K个就是慢指针
	SListNode* fast = head;
	for (size_t i = 0; i < k - 1; i++)
	{
		if (fast->_next != NULL)
		{
			fast = fast->_next;
		}
		else
		{
			return NULL;
		}
	}
	SListNode* slow = head;
	while (fast->_next != NULL)
	{
		fast = fast->_next;
		slow = slow->_next;
	}
	return slow;
}

//判断链表是否带环,求环的长度和入口
SListNode* IsCycle(SListNode* head)
{
	SListNode* slow, *fast;
	slow = fast = head;
	while (fast)
	{
		fast = fast->_next;
		slow = slow->_next;
		if (fast)
		{
			fast = fast->_next;
		}

		if (fast == slow)
		{
			return fast;//相遇点
		}
	}
	return NULL;//不是环
}

//求环的入口
SListNode* CycleEnter(SListNode* head, SListNode* meet)
{
	while (head != meet)
	{
		head = head->_next;
		meet = meet->_next;
	}
	return meet;
}

//求环的长度
DataType CycleLength(SListNode* head, SListNode* enter)
{
	DataType length = 1;
	SListNode* cur = enter;
	while (cur->_next != enter)
	{
		++length;
		cur = cur->_next;
	}
	return length;
}


//复杂链表的复制
typedef struct ComplexNode
{
	DataType _data;
	ComplexNode* _next;
	ComplexNode* _rand;
}ComplexNode;

ComplexNode* CopyList(ComplexNode* list)
{

}

void testSList()
{
	SListNode* list = NULL;

	SListInsertBack(&list, 1);
	SListInsertBack(&list, 3);
	SListInsertBack(&list, 2);
	SListInsertBack(&list, 4);
	SListInsertFront(&list, 6);
	SListInsertFront(&list, 5);
	SListPopFront(&list);
	SListPopFront(&list);
	SListPopBack(&list);
	/*DestorySList(&list);*/
	SListInsertBack(&list, 6);
	SListInsertBack(&list, 5);
	SListInsertBack(&list, 4);
	printSList(list);
	SListNode* p = NULL;
	p = FindSListNode(list, 3);
	without_Head_Insert(p, 33);

	printSList(list);
	printTailToHead(list);
	/*without_Head_DelSList(p);*/
	SListNode* JosephNode = NULL;
	JosephNode = JosephCycle(list, 4);
	cout << "幸存者为:" << JosephNode->_data << endl;

	SListNode*newList = NULL;
	SListInsertBack(&newList, 9);
	SListInsertBack(&newList, 2);
	SListInsertBack(&newList, 5);
	SListInsertBack(&newList, 10);
	SListInsertFront(&newList, 7);
	SListInsertFront(&newList, 1);
	BubbleSortList(newList);
	printSList(newList);

	SListNode*list1 = NULL;
	SListNode*list2 = NULL;
	SListInsertBack(&list1, 1);
	SListInsertBack(&list1, 3);
	SListInsertBack(&list1, 5);
	SListInsertBack(&list1, 7);
	SListInsertBack(&list2, 2);
	SListInsertBack(&list2, 4);
	SListInsertBack(&list2, 6);
	SListInsertBack(&list2, 8);
	SListInsertBack(&list2, 8);
	SListNode* n = NULL;
	n = MergeList(list1, list2);
	SListNode* backward;
	backward = Find_K_Backward(n, 4);
	cout << "倒数第4个为" << backward->_data << endl;
	printSList(n);

	SListNode* middle;
	middle = FindMiddle(n);
	cout << "中间点是" << middle->_data << endl;

	SListNode* Cycle = NULL;
	SListNode* pos, *tail;
	SListInsertBack(&Cycle, 1);
	SListInsertBack(&Cycle, 2);
	SListInsertBack(&Cycle, 3);
	SListInsertBack(&Cycle, 4);
	SListInsertBack(&Cycle, 5);
	SListInsertBack(&Cycle, 6);
	SListInsertBack(&Cycle, 7);
	SListInsertBack(&Cycle, 8);
	SListInsertBack(&Cycle, 9);

	pos = FindSListNode(Cycle, 5);
	//构成环
	tail = Cycle;
	while (tail->_next)
	{
		tail = tail->_next;
	}
	tail->_next = pos;

	SListNode* enter, *meet;
	meet = IsCycle(Cycle);
	cout << "相遇点是" << meet->_data << endl;
	enter = CycleEnter(Cycle, meet);
	cout << "入口点是" << enter->_data << endl;
	cout << "环的长度为" << CycleLength(Cycle, enter) << endl;

}

单链表和顺序表的区别

1、顺序表支持随机访问,单链表不支持。

2、顺序表插入/删除数据效率低,时间复杂度为O(N),单链表插入删除数据效率高,为O(1)

3、顺序表的cpu高速缓存效率高,单链表cpu高速缓存效率低。

 

二、双向链表:增加一个指针域指向它前一个结点---一般来说都是带头(无数据域)循环双向链表。

 

//双向链表的结构定义   带头双向循环链表
typedef struct DLinkNode
{
	struct DLinkNode *_next;
	struct DLinkNode *_prev;
	DataType _data;
}DLinkNode;


//创建双向链表结点
DLinkNode* CreateDListNode(DataType x)
{
	DLinkNode* node = (DLinkNode*)malloc(sizeof(DLinkNode));
	assert(node);
	node->_data = x;
	node->_prev = NULL;
	node->_next = NULL;
	return node;
}


//双向链表的初始化
DLinkNode* DListInit()
{
	DataType x = 0;
	DLinkNode* DList = CreateDListNode(x);
	DList->_next = DList;
	DList->_prev = DList;
	return DList;
}



//打印双向链表
void printDList(DLinkNode* head)
{
	if (head == NULL)
	{
		return;
	}
	DLinkNode* cur = head->_next;
	while (cur != head)
	{
		cout << cur->_data << "->";
		cur = cur->_next;
	}
	cout << "Head" << endl;
}
//尾插
void DListInsertBack(DLinkNode* head, DataType x)
{
	DLinkNode* tail = head->_prev;
	DLinkNode* newDNode = CreateDListNode(x);
	tail->_next = newDNode;
	newDNode->_prev = tail;
	newDNode->_next = head;
	head->_prev = newDNode;
}

//头插
void DListInsertHead(DLinkNode* head, DataType x)
{
	DLinkNode* next = head->_next;
	DLinkNode* newDNode = CreateDListNode(x);
	newDNode->_next = next;
	next->_prev = newDNode;
	head->_next = newDNode;
	newDNode->_prev = head;
}

//尾删
void DListPopBack(DLinkNode* head)
{
	assert(head);
	DLinkNode* end = head->_prev;
	DLinkNode* prev = end->_prev;
	prev->_next = head;
	head->_prev = prev;
	free(end);


}

//头删
void DListPopFront(DLinkNode* head)
{
	assert(head);
	DLinkNode* first = head->_next;
	DLinkNode* next = first->_next;
	next->_prev = head;
	head->_next = next;
	free(first);

}

//在一个节点前插入
void DListInsert(DLinkNode* pos, DataType x)
{
	assert(pos);
	DLinkNode* prev = pos->_prev;
	DLinkNode* newDNode = CreateDListNode(x);
	newDNode->_next = pos;
	pos->_prev = newDNode;
	newDNode->_prev = prev;
	prev->_next = newDNode;
}


//寻找结点
DLinkNode* FindDListNode(DLinkNode* head, DataType x)
{
	assert(head);
	DLinkNode* cur = head->_next;
	while (cur != head)
	{
		if (cur->_data == x)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}

//删除
void DListPop(DLinkNode* pos)
{
	assert(pos);
	DLinkNode* next = pos->_next;
	DLinkNode* prev = pos->_prev;
	prev->_next = next;
	next->_prev = prev;
	free(pos);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Rust数据结构是指在Rust编程语言中可以用于存储和组织数据的不同方式和类型。Rust提供了许多内置的数据结构,同时也可以使用各种第三方库来扩展这些数据结构。 常见的Rust数据结构包括: 1. 向量(Vectors):向量是一个动态长度的数组,可以在其中存储任意类型的数据。向量可以动态增长或缩小,也可以按索引访问元素。 2. 哈希映射(Hash Maps):哈希映射是一种键值对的数据结构,其中每个键都与一个唯一的值相关联。哈希映射的插入和查找操作的时间复杂度通常为O(1),因此在需要快速查找或数据去重的场景中非常有用。 3. 字符串(Strings):Rust中的字符串是一系列Unicode标量值的集合。它们可以通过字面量、转换或动态构建来创建和操作。Rust还提供了许多与字符串相关的方法和操作符。 4. 切片(Slices):切片是对数组或向量的引用,允许您引用整个集合或仅引用集合的一部分。切片非常适用于通过传递指定范围的数据来减少内存占用和提高性能的情况。 此外,还有很多其他数据结构可以在Rust中使用,例如堆栈、队列、链表等。Rust还提供了许多强大的工具和概念,如所有权、借用和生命周期,这些可以帮助开发人员安全地管理数据结构的访问和修改。 通过使用各种数据结构,Rust为开发人员提供了灵活和高效的方式来存储和操作数据,使他们能够更轻松地构建稳健和高性能的应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值