链表-------常见题型(面试题)

1.从尾头到打印单链表

void PrintReverse(ListNode *first)
{
	ListNode *end = NULL;
	while (end != first)
	{
		ListNode *cur = first;
		
		//找到要打印的结点
		while (cur->next != end)
		{
			cur = cur->next;
		}
		printf("%d  ", cur->data);
		end = cur;
	}
	printf("\n");
}

用递归的方法求解:

a1    a2    a3    ........    an

递归:

  • a1 结果已知
  • a(k+1) 的结果可以通过 a(k) 结果推导出来
//递归
void PrintReverseRecursion(ListNode *first)
{
	if (first->next == NULL){
		printf("%d  ", first->data);
	}
	else{
		PrintReverseRecursion(first->next);
		//链表中除了 first 之外的所有结点都逆序打印了
		printf("%d  ", first->data);
	}
}

2.逆转/反转单链表

传入链表,进行逆置/反转,返回逆置/反转后的链表的第一个结点地址,原链表将无效
1-->2-->3-->4-->5
5-->4-->3-->2-->1 

头删---->头插

ListNode *ReverseList(ListNode *first)
{
	ListNode *cur = first;
	ListNode *node;
	ListNode *result = NULL;

	while (cur != NULL)
	{
		//从原来链表中头删(光摘下来,没有真正删除)
		node = cur;
		cur = cur->next;

		//node 就是被摘下来的结点
		node->next = result;
		result = node;
	}
	return result;
}

方法二: 

不调整值的位置,直接改变链表指针指向的方向。即:

1-->2-->3-->4-->5-->NULL
NULL<--1<--2<--3<--4<--5

ListNode *ReverseList2(ListNode *first)
{
	ListNode *p1 = NULL;
	ListNode *p2 = first;
	ListNode *p3 = first->next;

	while (p2 != NULL)
	{
		p2->next = p1;

		p1 = p2;
		p2 = p3;
		if (p3 != NULL)
		{
			p3 = p3->next;
		}
	}

	return p1;
}

3.删除一个无头单链表的非尾节点(不能遍历链表)

void RemoveNoFirst(ListNode *pos)
{
	pos->data = pos->next->data;
	ListNode *del = pos->next;
	pos->next = pos->next->next;

	free(del);
}

4.在无头单链表的一个节点前插入一个节点(不能遍历链表)

void InsertNoFirst(ListNode *pos, DataType data)
{
	ListNode *cur = pos;
	cur->next = pos->next;
	pos->next = cur;

	pos->data = data;
}

5.约瑟夫环

ListNode *JosephCycle(ListNode *first, int k)
{
	//第一步,链表构成环
	ListNode *tail = first;
	while (tail->next != NULL)
	{
		tail = tail->next;
	}
	//tail就是最后一个节点
	tail->next = first;

	//第二步
	ListNode *cur = first;
	//结束条件是链表中只有一个结点
	while (cur->next == NULL){
		ListNode *prev = NULL;
		for (int i = 0; i < k - 1; i++){
			prev = cur;
			cur = cur->next;
		}

		//cur就是我们要删除的结点
		prev->next = cur->next;
		free(cur);

		//让循环继续
		cur = prev->next;
	}

	cur->next = NULL;
	return cur;

}

6.合并两个有序链表,合并后依然有序(升序)

ListNode* MergeOrderedList(ListNode *list1, ListNode *list2)
{
	assert(list1);
	assert(list2);

	ListNode *p1 = list1;
	ListNode *p2 = list2;
	ListNode *result = NULL;

	while (p1 != NULL && p2 != NULL)
	{
		if (p1->data < p2->data){
			ListPushBack(&result, p1->data);
			p1 = p1->next;
		}
		else{
			ListPushBack(&result, p2->data);
			p2 = p2->next;
		}
	}

	// 一个链表为空了
	if (p1 == NULL) {
		while (p2 != NULL){
			ListPushBack(&result, p2->data);
			p2 = p2->next;
		}
	}

	if (p2 == NULL) {
		while (p1 != NULL){
			ListPushBack(&result, p1->data);
			p1 = p1->next;
		}
	}

	return result;
}

优化:

ListNode * MergeOrderedListAd(ListNode *list1, ListNode *list2)
{
	ListNode *cur1 = list1;
	ListNode *cur2 = list2;
	ListNode *result = NULL;  //结果链表
	ListNode *tail = NULL;    //结果链表的最后一个结点,方便尾插
	ListNode *next;           //保存遍历过程的下一个结点
	ListNode *node;

	while (cur1 != NULL && cur2 != NULL)
	{
		if (cur1->data <= cur2->data)
		{
			node = cur1;
		}
		else
		{
			node = cur2;
		}
		next = node->next;
		if (result != NULL)
		{
			//链表不为空,则做尾插处理
			tail->next = node;
		}
		else
		{
			//保存链表的下一个结点,让循环继续
			result = node;
		}

		node->next = NULL;
		//保存新的最后一个结点
		tail = node;

		if (node == cur1)
		{
			cur1 = next;
		}
		else
		{
			cur2 = next;
		}

		//一个链表空了
		if (cur1 == NULL)
		{
			tail->next = cur2;
		}
		if (cur2 == NULL)
		{
			tail->next = cur1;
		}

		return result;
	}
}

7.求两个已排序单链表中相同的数据。

void Unionset(ListNode *list1, ListNode *list2)
{
	ListNode *cur1 = list1;
	ListNode *cur2 = list2;

	while (cur1 != NULL && cur2 != NULL)
	{
		if (cur1->data < cur2->data)
		{
			cur1 = cur1->next;
		}
		else if (cur1->data > cur2->data)
		{
			cur2 = cur2->next;
		}
		else
		{
			printf("%d  ", cur1->data);
			cur1 = cur1->next;
			cur2 = cur2->next;
		}
	}

	printf("\n");
}

另一种方法:

void UnionsetDup(ListNode *list1, ListNode *list2)
{
	ListNode *cur1 = list1;
	ListNode *cur2 = list2;
	DataType data;

	while (cur1 != NULL && cur2 != NULL)
	{
		if (cur1->data < cur2->data)
		{
			cur1 = cur1->next;
		}
		else if (cur1->data > cur2->data)
		{
			cur2 = cur2->next;
		}
		else
		{
			printf("%d  ", cur1->data);
			data = cur1->data;

			while (cur1 != NULL && cur1->data == data)
			{
				cur1 = cur1->next;
			}
			while (cur2 != NULL && cur2->data == data)
			{
				cur2 = cur2->next;
			}
		}
	}

	printf("\n");
}

8.查询单链表的中间结点,要求只遍历一遍

void FindMid(ListNode *first)
{
	ListNode *fast = first;
	ListNode *slow = first;

	while (1)
	{
		fast = fast->next;
		if (fast == NULL)
		{
			break;
		}
		fast = fast->next;
		if (fast == NULL)
		{
			break;
		}
		slow = slow->next;
	}
	printf("%d\n", slow->data);
}

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

void FindTailK(ListNode *first, int k)
{
	ListNode *forward = first;
	ListNode *backward = first;

	while (k--)
	{
		forward = forward->next;
	}

	while (forward != NULL)
	{
		forward = forward->next;
		backward = backward->next;
	}

	printf("%d\n", backward->data);
}

10.打印链表

void PrintResult(ListNode* result)
{
	ListNode *cur;

	for (cur = result; cur != NULL; cur = cur->next)
	{
		printf("%d->", cur->data);
	}
	printf("NULL\n");
}

11.测试函数和主函数:

//测试
void TestPrintReverse()
{
	ListNode *first = NULL;
	ListPushBack(&first, 1);
	ListPushBack(&first, 2);
	ListPushBack(&first, 3);
	ListPushBack(&first, 4);
	ListPushBack(&first, 5);

	printf("倒序打印:");
	PrintReverse(first);
	printf("递归:");
	PrintReverseRecursion(first);
	printf("\n");

	ListNode *result = ReverseList(first);
	printf("逆置打印:");
	PrintResult(result);

	ListNode *sur = JosephCycle(first, 4);
	printf("约瑟夫环:%d \n", sur->data);

	ListNode *list1 = NULL;
	ListPushBack(&list1, 1);
	ListPushBack(&list1, 2);
	ListPushBack(&list1, 3);
	ListPushBack(&list1, 5);
	ListPushBack(&list1, 7);

	ListNode *list2 = NULL;
	ListPushBack(&list2, 4);
	ListPushBack(&list2, 6);
	ListPushBack(&list2, 8);
	ListPushBack(&list2, 9);

	printf("合并链表:");
	PrintResult(MergeOrderedList(list1, list2));

	printf("找相同数据:");
	ListNode *list3 = NULL, *list4 = NULL;
	ListPushBack(&list3, 1);
	ListPushBack(&list3, 3);
	ListPushBack(&list3, 4);
	ListPushBack(&list3, 5);

	ListPushBack(&list4, 1);
	ListPushBack(&list4, 2);
	ListPushBack(&list4, 4);

	Unionset(list3, list4);
	UnionsetDup(list3, list4);

	printf("找中间结点:");
	FindMid(first);

	printf("倒数第k个结点:");
	FindTailK(list1, 3);

}

int main()
{
	TestPrintReverse();
	return 0;
}

12.附:链表实现的源文件"List.c"

注明:上面的函数实现调用了这里的函数

#include <stdlib.h>
#include <assert.h>

typedef int DataType;

typedef struct ListNode {
	DataType data;
	struct ListNode *next;
}	ListNode;

// 初始化/销毁
void ListInit(ListNode **ppFirst)
{
	assert(ppFirst != NULL);
	*ppFirst = NULL;
}

void ListDestroy(ListNode **ppFirst)
{
	// TODO:
	*ppFirst = NULL;
}

// 增删查改
static ListNode * CreateNode(DataType data)
{
	ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));
	assert(newNode);
	newNode->data = data;
	newNode->next = NULL;

	return newNode;
}

//头插
void ListPushFront(ListNode **ppFirst, DataType data)
{
	assert(ppFirst != NULL);
	// 考虑特殊情况,链表为空 *ppFirst == NULL

	// 正常情况
	// 1. 指针 vs 指向空间;从堆上申请空间
	ListNode *newNode = CreateNode(data);
	newNode->next = *ppFirst;

	*ppFirst = newNode;
}

//尾插
void ListPushBack(ListNode **ppFirst, DataType data)
{
	ListNode *newNode = CreateNode(data);

	// 特殊情况,找倒数第一个 -> 至少有一个,所以特殊情况是链表为空
	if (*ppFirst == NULL) {
		*ppFirst = newNode;
		return;
	}

	// 通常情况
	ListNode *cur = *ppFirst;
	while (cur->next != NULL) {
		cur = cur->next;
	}

	// cur 就是最后一个结点
	cur->next = newNode;
}

// 删除
//头删
void ListPopFront(ListNode **ppFirst)
{
	assert(ppFirst != NULL);	// 变量地址不为 NULL
	assert(*ppFirst != NULL);	// 不能是空链表

	ListNode *del = *ppFirst;
	*ppFirst = del->next;

	free(del);	// 谨记
}


void ListPopBack(ListNode **ppFirst)
{
	assert(ppFirst != NULL);	// 变量地址不为 NULL
	assert(*ppFirst != NULL);	// 不能是空链表

	// 链表中只有一个结点
	if ((*ppFirst)->next == NULL) {
		free(*ppFirst);
		*ppFirst = NULL;
		return;
	}

	// 正常情况
	ListNode *del;
	ListNode *cur = *ppFirst;

	while (cur->next->next != NULL) {
		cur = cur->next;
	}

	del = cur->next;
	cur->next = NULL;
	free(del);
}


// 查找
ListNode * ListFind(ListNode *first, DataType data)
{
	// 顺序查找,去遍历
	for (ListNode *cur = first; cur != NULL; cur = cur->next) {
		if (data == cur->data) {
			return cur;
		}
	}

	return NULL;
}

// 在结点前做插入(结点 pos 肯定在链表中 && pos 不是空【链表不是空】)
void ListInsert(ListNode **ppFirst, ListNode *pos, DataType data)
{
	// 头插
	if (*ppFirst == pos) {
		ListPushFront(ppFirst, data);
		return;	// 记得 return,否则下面加上 else
	}

	ListNode *cur = *ppFirst;
	// 找 pos 的前一个结点
	while (cur->next != pos) {
		cur = cur->next;
	}

	// 插入新结点
	ListNode *newNode = CreateNode(data);	// 一定要申请空间
	newNode->next = cur->next;	// or pos;
	cur->next = newNode;
}

// 删除指定结点(结点 pos 肯定在链表中 && pos 不是空【链表不是空】
void ListErase(ListNode **ppFirst, ListNode *pos)
{
	// 头删
	if (*ppFirst == pos) {
		ListPopFront(ppFirst);
		return;	// 记得 return,否则下面加上 else
	}

	ListNode *cur = *ppFirst;
	// 找 pos 的前一个结点
	while (cur->next != pos) {
		cur = cur->next;
	}

	cur->next = pos->next;
	free(pos);	// 记得
}

void Test()
{
	ListNode *first;
	ListInit(&first);	// 传值 1. first	传地址 2. &first
	// 链表是一条空链表, first == NULL

	// first 会变化
	ListPushBack(&first, 1);

	// 以后 first 不变了
	ListPushBack(&first, 2);
	ListPushBack(&first, 3);

	ListNode *result = ListFind(first, 2);	// 传地址 or 传值?
	ListInsert(&first, result, 0);
}

猜你喜欢

转载自blog.csdn.net/qq_40933663/article/details/82890474