顺序表收尾
二分查找 是作用于顺序表上的,顺序表的优势:可以随机访问
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。