双向带头循环链表,OJ题

顺序表收尾

二分查找 是作用于顺序表上的,顺序表的优势:可以随机访问

void SLTInsertAfter(SLTNode* pos, SLTDateType x);//在pos位置之后插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);//在pos之前插入

以上这两种插入都要判断assert(pos),因为pos是由SListFind函数返回,不存在时返回值为NULL。

SLTInsertAfter函数的参数为啥是1级指针?因为这里要改变的是结构体的成员的内容,用结构体的指针就可以改变了。

void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);
    SLTNode* newnode = BuySLTNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

而SLTInsert有可能在头指针前面插入,会改变指针的指向(内容),所以要用二级指针。

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
    assert(pos);
    SLTNode* newnode = BuySLTNode(x);
    
    if(pos == *pphead)
    {
        SLTPushFornt(pphead, x);//直接调用头插函数
    }
    else
    {
        SLTNode* cur = *pphead;
    	while(cur->next != pos)
    	{
        	cur = cur->next;
    	}
        newhead->next = cur->next->next;
        cur->next = newhead;
    }
    
}

讲解了两道OJ题,这两道题都可以用带哨兵位的单向链表。用到尾插的可以采用带哨兵位的头节点。

203.移除链表元素

删除链表中等于给定值 val 的所有结点。

21.合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。

这两道题正常思路都是用不带头单向链表,如果用不带头单向链表,在处理尾插的时候,第一次都需要先判断是否为空

if(tail == NULL)
{
	head = tail = cur;
}

带头单向链表,在处理尾插时,直接插入就行。

guard = tail = (struct SList*)malloc(sizeof(struct SList));
guard = tail = NULL;
//do
newhead = guard->next;
free(guard);
return newhead;

双向带头循环链表代码

代码如下

//List.h
#pragma once

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

typedef int LTDataType;

typedef struct ListNode
{
	struct ListNode* prev;
	LTDataType data;
	struct ListNode* next;
}LTNode;

LTNode* BuyListNode(LTDataType x);
void ListPrint(LTNode* phead);
//初始化 这里哨兵位用phead,第一个有效数据plist
LTNode* ListInit(LTNode* phead);
//尾插尾删
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
//头插头删
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
//查找
LTNode* ListFind(LTNode* phead, LTDataType x);
//任意位置处插入 删除
void LTInsert(LTNode* phead, LTNode* pos, LTDataType x);
void LTErase(LTNode* phead, LTNode* pos);
//销毁
void LTDestory(LTNode* phead);
//判空
bool LTEmpty(LTNode* phead);
//计数
size_t LTSize(LTNode* phead);
//List.c
#include "List.h"

#define _CRT_SECURE_NO_WARNINGS 1

LTNode* BuyListNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	//开辟失败 返回
	if (newnode == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	//开辟成功 赋值
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

void ListPrint(LTNode* phead)
{
	assert(phead);
	//跳过哨兵位
	LTNode* cur = phead->next;
	//又循环回来,依次打印
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

LTNode* ListInit(LTNode* phead)
{
	//哨兵位不存储有效数据
	phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

void LTPushBack(LTNode* phead, LTDataType x)
{
	LTNode* newnode = BuyListNode(x);
	//找尾
	LTNode* tail = phead->prev;

	tail->next = newnode;
	phead->prev = newnode;
	newnode->prev = tail;
	newnode->next = phead;
}

void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead != phead->next);//判空
	//保存倒数第1个节点,倒数第二个节点
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;
	//链接
	tailprev->next = phead;
	phead->prev = tailprev;
	free(tail);
}

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = BuyListNode(x);
	//保留第一个节点
	LTNode* first = phead->next;
	//链接
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;
}

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead != phead->next);

	LTNode* first = phead->next;
	LTNode* second = first->next;
	
	phead->next = second;
	second->prev = phead;
}

LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
			return cur;//找到就返回
		cur = cur->next;
	}
	return NULL;
}

void LTInsert(LTNode* phead, LTNode* pos, LTDataType x)
{
	assert(pos && phead);
	LTNode* newnode = BuyListNode(x);
	LTNode* prev = pos->prev;//在pos前插入

	pos->prev = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	prev->next = newnode;

	//直接复用尾插,在pos前插入
	//LTPushBack(pos, x);
}

void LTErase(LTNode* phead, LTNode* pos)
{
	assert(phead);
	assert(phead != phead->next);

	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	free(pos);
	//链接前后两个节点
	prev->next = next;
	next->prev = prev;
}

void LTDestory(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

bool LTEmpty(LTNode* phead)
{
	/*if (phead != phead->next)
		return false;
	else
		return true;*/
	return phead == phead->next;
}

size_t LTSize(LTNode* phead)
{
	assert(phead);
	size_t size = 0;//用于计数

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

链表OJ题

链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

思路1:快指针先走k步,然后快指针和漫指针一起一步一步地走;

思路2:2次遍历。第一次遍历记录链表总个数,第二次遍历找节点。

CM11 链表分割

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

思路:创建两个新的带头单向链表,第一个放小的,第二个放大的,再把这两个链接。

876. 链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

思路1:快慢指针法,定义两个指针fast和slow,fast一次走两步,slow一次走一步。要区别奇数偶数情况。0

21.合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

思路:从头开始,取两个链表中小的那个尾插到新链表中。

141.环形链表 判断链表是否有环

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

**思路:**快慢指针法,追及相遇问题。快指针先进入环,慢指针再进入环,如果快指针==慢指针,则有环。

**问题:**1、请证明slow和fast一定会在环里面相遇?有没有可能永远追不上?

结论:当slow一次走1步,fast一次走2步时,一定可以追上。

slow进环以后,fast正式开始追了,假设fast和slow之间的距离为N,当slow走1步,fast走2步,距离会变成N+1-2=N-1,当t趋于无穷时,N会变为0,一定能追上。

2、slow一次走1步,fast一次走3步行不行?slow一次走1步,fast一次走4步行不行?

结论:当slow一次走1步,fast一次走3步时,可能追得上,也可能追不上且死循环。

slow进环以后,fast正式开始追了,假设fast和slow之间的距离为N,当slow走1步,fast走3步,距离会变成N+1-3=N-2,当步长趋于无穷时,如果N是奇数,N会变成-1;如果N是偶数,N最终会变成0,则追上。

距离N为0代表相遇,距离N为-1代表fast反超slow,进入新的追逐fast追slow,他们之间的距离变成C-1。

距离N=-1进入新的追逐,fast距离slow的距离变成C-1,C为环的长度。则随着步长的增加,他们之间的距离变为(C-1)+1-3,如果(C-1)是偶数,(C-1)+1-3最终会变成0,则追上;如果(C-1)是奇数,(C-1)+1-3最终会变成-1,又开始新一轮循环。

总结:当fast和slow之间的距离为奇数时,意味着,快要追上时,距离又会变成-1,fast反超slow,又开始一轮新的循环,永远追不上,陷入死循环。

同理fast走4步,则只有fast和slow之间的距离为3的整数倍时,才能追上。

142.环形链表 II 求环形链表的入口点

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。

思路:快慢指针找到环 + temp指针找到环的入口。

首先给出**结论:**当fast和slow相遇时,此时定义一个temp指针,指向 head, 让slow 和 temp同时移动,他们相等的时候就是环的入口。推论:当slow进环后,在一圈内,fast必定追上slow。因为因为他俩之间的距离一定是小于环的长度,而fast指针走的路程是slow的路程的2倍。即slow走了一圈,fast走了两圈,肯定在这个过程中遇上。

160.相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

思路:两个链表AB各自遍历,得到各自的节点数lenA,lenB,【此处遍历结束后,会得到tailA和tailB,判断一下tailA和tailB是否相等,不相等表示没有相交】,然后根据(lenA-LenB = gap)的差值,哪个链表长,哪个链表就先走gap步。接下来两个链表就变成一样长,再while循环判断是否相交,相交返回这个节点,没相交返回NULL。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值