顺序表和链表

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析2

在这里插入图片描述


👉🏻线性表

概念

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组链式结构的形式存储。

在这里插入图片描述

顺序表

概念

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存
储。在数组上完成数据的增删查改

顺序表一般分为:

  • 静态顺序表:使用定长数组存储元素
  • 动态顺序表:使用动态开辟的数组存储(空间不够则增容)

用顺序表实现增删查改

SeqList.c

#include "SeqList.h"
SL s;
void Test1()
{
	PushEndSL(&s,  1);
	PushEndSL(&s, 2);
	PushEndSL(&s, 3);
	PushEndSL(&s, 4);
	PushEndSL(&s, 5);
	PushEndSL(&s, 6);
	/*PushFrontSL(&s, 8);
	PushFrontSL(&s, 9);
	PushFrontSL(&s, 10);
	PushFrontSL(&s, 11);*/
	/*DelEndSL(&s);
	DelFrontSL(&s);*/
	//InsertSL(&s, 2, 2);
	/*EraseSL(&s, 4);*/
	/*ModifySL(&s, 2);*/
	PrintSL(&s);
}
//int main()
//{
//	InitSL(&s);
//	Test1();
//	DestroySL(&s);
//	return 0;
//}

SeqList.c

#include "SeqList.h"
void CheckCapacitySL(SL* psl)
{
	if (psl->sz == psl->capacity)//如果容量已满
	{
		SeqListType* tmp = (SeqListType*)realloc(psl->a, sizeof(SeqListType) * 2*(psl->capacity));//每次先扩容2倍大小
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		psl->a = tmp;
		psl->capacity *= 2;
	}
}
void InitSL(SL* psl)
{
	psl->a = (SeqListType*)malloc(sizeof(SeqListType) * 4);//一开始初始化先开辟4大小空间
	if (psl->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	psl->sz = 0;
	psl->capacity = 4;
}
void DestroySL(SL* psl)
{
	free(psl->a);
	psl->a = NULL;
	psl->sz = 0;
	psl->capacity = 0;
}
void PushEndSL(SL* psl, SeqListType x)
{
	CheckCapacitySL(psl);//检查容量
	psl->a[psl->sz] = x;
	psl->sz++;
}
void PushFrontSL(SL* psl, SeqListType x)
{
	CheckCapacitySL(psl);//检查容量
	for (int i = psl->sz - 1; i >= 0; i--)
	{
		psl->a[i + 1] = psl->a[i];
	}
	psl->a[0] = x;
	psl->sz++;
}
void DelEndSL(SL* psl)
{
	//assert(psl->sz>0);//为假报错
	if (psl->sz == 0)//如果sz为0,则不能够再减了
		return;
	psl->sz--;
}
void DelFrontSL(SL* psl)
{
	if (psl->sz == 0)//如果sz为0,则不能够再减了
		return;
	int i = 1;
	for (i = 1; i < psl->sz; i++)
	{
		psl->a[i - 1] = psl->a[i];
	}
	psl->sz--;
}
void InsertSL(SL* psl, int pos, SeqListType x)
{
	CheckCapacitySL(psl);//检查容量
	assert(pos >= 0 && pos <= psl->sz);//判断pos的位置合不合理
	int i = 0,num= psl->sz;
	for (i = 0; i < psl->sz - pos; i++)
	{
		psl->a[num] = psl->a[num - 1];
		num--;
	}
	psl->a[pos] = x;
	psl->sz++;
}
void EraseSL(SL* psl, int pos)
{
	assert(psl->sz > 0);//如果为0,再减sz就不礼貌喽
	assert(pos>=1&&pos<=psl->sz);
	int i = 0, num = pos;
	for (i = 0; i < psl->sz - pos; i++)
	{
		psl->a[num-1] = psl->a[num];
		num++;
	}
	psl->sz--;
}

int FindSL(SL* psl, SeqListType x)
{
	for (int i = 0; i <psl->sz; i++)
	{
		if (psl->a[i] == x)
			return i;//返回下标
	}
	return -1;
}
void ModifySL(SL* psl, int pos, SeqListType x)
{
	assert(pos > 0 && pos <= psl->sz);
	psl->a[pos - 1] = x;
}
void PrintSL(SL* psl)
{
	int i = 0;
	for (i = 0; i < psl->sz; i++)
	{
		printf("%d\n", psl->a[i]);
	}
}

SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//静态顺序库
typedef int SeqListType;//为了后期方便改存储的数据类型
#define N 10
//静态顺序库
//struct SeqList
//{
//	SeqListType arr[N];
//	int sz;
//};

//动态顺序库
typedef struct SeqList
{
	SeqListType* a;
	int sz;//存储的有效数据的个数
	int capacity;//当前库的容量
}SL;
void InitSL(SL* psl);
void DestroySL(SL* psl);
void PushEndSL(SL* psl, SeqListType x);//尾部插入数据
void PushFrontSL(SL* psl, SeqListType x);//头部插入数据
void DelEndSL(SL* psl);//尾部删除数据
void DelFrontSL(SL* psl);//头部删除数据

void InsertSL(SL* psl, int pos, SeqListType x);//在内部插入数据
void EraseSL(SL* psl, int pos);//在内部删除数据

int FindSL(SL* psl, SeqListType x);//查找一个数字
void ModifySL(SL* psl, int pos, SeqListType x);//修改某数字


void PrintSL(SL* psl);//打印数据

👉🏻 链表

概念

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。

在这里插入图片描述

简单链表实现

Slist Test.c

#include "Slist.h"

void Test_1()
{
	SL* p = NULL;
	PushFrontSL(&p, 1);
	PushFrontSL(&p, 2);
	PushFrontSL(&p, 3);
	PushFrontSL(&p, 4);
	PrintSL(p);
}
int main()
{
	Test_1();
	return 0;
}

Slist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Slist.h"

void PushFrontSL(SL** phead, SlistType x)//为何要二级指针?若我们改的是一级指针本身(即地址)
//地址本身就是一个数据,即一个数值,当我们对地址进行“动手脚”时,在传参形式上,就不能叫做传址,而是传值了
//所以如果要修改地址,则要使用二级指针才行
{
	SL* newnode = (SL*)malloc(sizeof(SL*));//开辟新空间
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = *phead;
	*phead = newnode;
}
void PrintSL(SL* phead)
{
	SL* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
	return;
}

为何要二级指针?若我们改的是一级指针本身(即地址)
地址本身就是一个数据,即一个数值,当我们对地址进行“动手脚”时,在传参形式上,就不能叫做传址,而是传值了

Slist.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SlistType;
typedef struct Slist
{
	SlistType data;
	struct Slist* next;
}SL;

void PushFrontSL(SL** phead, SlistType x);
void PrintSL(SL* phead);

在这里插入图片描述

🖌链表进行尾插

代码示例

SL* CreatNewnode(SlistType x)//创建一个开辟新空间的函数(适用于尾插开辟新空间)
{
	SL* newnode = (SL*)malloc(sizeof(SL*));//开辟新空间
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void PushEndSL(SL* phead, SlistType x)
{
	SL* tail = phead;
	if (tail != NULL)
	{
		tail = tail->next;
	}
	SL* newnode = CreatNewnode(x);
	tail = newnode;
}

先说结论:上述的代码是错误的。接下来,通过图解看下该代码的具体流程
在这里插入图片描述

这么一看,好像没什么问题啊。
但这里有个很致命的问题是,在PushEndSL函数中的变量tail和newnode都是局部变量
这就会导致上述代码中

SL* newnode = CreatNewnode(x);
	tail = newnode;

这一部分,在函数结束后,newnode和tail作为局部变量会销毁,而原本newnode指向的那个malloc开辟的空间成为“孤空间”(指向该空间的指针消失了),而该空间是不会销毁的,因为是动态开辟,其在内存中的堆区,而该孤空间得不到释放,会造成内存泄漏
而至于tail,就更不用说了,也是该销毁的销毁,从而无法真正做到将链表给连接起来

所以我们该如何改良呢?

void PushEndSL(SL* phead, SlistType x)
{
	SL* tail = phead;
	if (tail->next != NULL)
	{
		tail = tail->next;
	}
	SL* newnode = CreatNewnode(x);
	tail->next = newnode;
}

我们只是简单的将newnode处的地址赋给tail->next处,因为该处位于开辟的动态空间中,不会随着函数的结束而销毁,这才把节点地址存储起来了,而这里也将判断语句改为tail->next != NULL,当next指向的为空指针时,停止访问,进行开辟新空间。

但实际上这里代码还是存在BUG,就是当phead的值为NULL时,此时tail->next对空指针进行解引用会出现错误,所以,在这里我们需要考虑两种情况,空链表非空链表
优化如下👇🏻

void PushEndSL(SL* phead, SlistType x)
{
	SL* newnode = CreatNewnode(x);
	//1.空链表
	if (phead == NULL)
	{
		phead = newnode;
	}
	//2.非空链表
	else
	{
		SL* tail = phead;
		if (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

如果到这里,你觉得是不是已经差不多了,我只能说:少年,还太年轻了
在这里插入图片描述
哪里又有问题呢?
通过仔细观察,我们会发现,这里我们对地址进行了传值操作,phead只是作为一份临时拷贝,对其赋值,根本影响不了实参,所以,要想实现最后的成功,我们要动用二级指针了,进行传址操作
优化如下👇🏻

void PushEndSL(SL** phead, SlistType x)
{
	SL* newnode = CreatNewnode(x);
	//1.空链表
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	//2.非空链表
	else
	{
		SL* tail = *phead;
		if (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

嗯,终于大功告成了,此时我们终于能实现尾插的功能了。

🖌链表进行尾删

我们进行尾删的思路为:

  • 找尾:找到节点中next指向的空间为空指针
  • 释放:释放该节点空间
  • 置空:将指向该节点空间的指针置为NULL,也就是倒数第二个节点的next指针

需要考虑的三种情况

  1. 没有节点,即空链表情况
  2. 节点为1个
  3. 多个节点

尾删功能实现👇🏻

void EraseEndSL(SL** pphead)
{
	SL* tail = *pphead;
	//1.空链表情况,温柔的检查
	//if (*pphead==NULL)
	//{
	//	return;
	//}
	//暴力检查
	assert(*pphead);
	//2.一个节点
	if (tail->next == NULL)
	{
		free(tail);
		tail = NULL;
	}
	else
	{
		while ((tail)->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

🖌链表进行头删

头删的思路为
将第一个节点空间释放,让phead指向第二个节点空间
也需要考虑三种情况

  1. 没有节点,即空链表情况
  2. 节点为1个
  3. 多个节点

头删功能实现👇🏻

void EraseFrontSL(SL** pphead)
{
	//1.空链表
	assert(*pphead);
	//2.一个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);//释放空间
		*pphead = NULL;
	}
	else
	{
		SL* tail = *pphead;
		/*SL* tail_next = tail->next;*/
		*pphead = (*pphead)->next;
		free(tail);
		/*tail = tail_next;*/
	}
}

🖌链表进行内部插入(前插)

void InsertFrontSL(SL** pphead, SL* pos, SlistType x)
{
	assert(pphead);//因为phead不可能为NULL,所以需要断言一下(如果出现NULL则不合理)
	assert(pos);
	/*assert(*pphead);*///这个不能断言,因为可以允许*phead为空指针,因为空指针也能进行插入数据
	if ((*pphead)->next == NULL)
	{
		PushFrontSL(pphead, x);//如果节点只有一个,就进行头插
	 }
	else
	{
		SL* cur = *pphead;
		while (1)
		{
			if (cur->next == pos)
				break;
			cur = cur->next;
		}
		SL* newnode = CreatNewnode(x);
		newnode->next = pos;
		cur->next = newnode;
	}
}

🖌链表进行内部插入(后插)

void InsertEndSL(SL** pphead, SL* pos, SlistType x)
{
	assert(pphead);//因为phead不可能为NULL,所以需要断言一下(如果出现NULL则不合理)
	assert(pos);
	//if ((*pphead)->next == NULL)
	//{
	//	PushEndSL(pphead, x);//如果节点只有一个,就进行尾插
	//}
	/*else
	{*/
		SL* cur = *pphead;
		while (1)
		{
			if (cur == pos)
				break;
			cur = cur->next;
		}
		SL* newnode = CreatNewnode(x);
		newnode->next = pos->next;
		cur->next = newnode;
	/*}*/
}

🖌链表进行内部删除(pos位置删除)

void EraseSL(SL** pphead, SL* pos)
{
	assert(pphead && pos && *pphead);
	SL* cur = *pphead;
	if (cur == pos)
	{
		EraseFrontSL(pphead);//一个节点时进行头删
	}
	while (1)
	{
		if (cur->next == pos)
			break;
		cur = cur->next;
	}
	cur->next = pos->next;
	free(pos);
}

🖌链表进行内部删除(pos位置后一个节点删除)

void EraseAfterSL(SL** pphead, SL* pos)
{
	assert(pphead && pos && *pphead);
	assert(pos->next);
	SL* pos_next =pos->next ;
	pos->next = pos_next->next;
	free(pos_next);
}

🖌链表进行查找

SL* FindSL(SL* phead, SlistType x)
{
	SL* cur = phead;
	while (cur)//当cur指针指向的为NULL,则无数据,停止循环
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;//找不到返回空指针
}

👉🏻 链表OJ题

移除链表元素

在这里插入图片描述

法一:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val){
   struct ListNode* prev=NULL,*cur=head;
   while(cur)
   {
       if(cur->val==val)
       {
           if(prev)
           {
                  prev->next=cur->next;
                free(cur);
               cur=prev->next;
           }
           else//如果第一个就是,进行头删
           {
                cur=head->next;
               free(head);
               head=cur;
           }
       }
       else
       {
           prev=cur;
           cur=cur->next;
       }
   }
   return head;
}

法二:尾插

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* newnode=NULL,*cur=head,*tail=NULL;
while(cur)
{
    if(cur->val!=val)//如果不等于进行尾插(在newnode中)
    {
       if(tail==NULL)//如果一开始第一个就要进行尾插
       {
           newnode=tail=cur;
       }
       else
       {
            tail->next=cur;//进行插入
            tail=tail->next;//目前已经存放进去数据的位置(最前位)
       }
       cur=cur->next;
       tail->next=NULL;//防止最后一个值是val,而将最后一个值free后,tail->next不能再指向这个空间了,所以令其为NULL
    }
    else
    {
        struct ListNode* del=cur;//存储起来要删除的该节点
        cur=cur->next;
         free(del);
    }
}
return newnode;
}

链表的中间节点

在这里插入图片描述

快慢指针作法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* middleNode(struct ListNode* head){
      struct ListNode* slow=head,*fast=head;
      if(head==NULL)
      return NULL;
      while(fast!=NULL&&fast->next!=NULL)
      {
            slow=slow->next;
            fast=fast->next->next;
      }
      return slow;
}

链表中倒数第k个结点

在这里插入图片描述
牛客链接:链表中倒数第k个结点

法一:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    struct ListNode* tail=pListHead,*cur=pListHead;
    if(pListHead==NULL)
    return NULL;
    int i=0;
    for(i=0;i<k;i++)
    {
         if(cur==NULL)
            break;
        cur=cur->next;
    }
    if(i<k)//此时整个链表长度小于k
    return NULL;
    while(cur)
    {
        tail=tail->next;
        cur=cur->next;
    }
    return tail;
}

反转链表

反转链表

法一:三指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head)
{
      struct ListNode* prev=NULL,*tail=head,*cur=head;
      if(head==NULL)
      return NULL;
      if(cur->next==NULL)
      return cur;
      cur=cur->next;//cur先走一步
      while(1)
      {
        tail->next = prev;
		prev = tail;
		if (cur->next != NULL)
		{
            tail = cur;
			cur = cur->next;
		}
		else
			break;
      }
      cur->next=tail;
      return cur;
}

示例代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

/*双指针+一个暂时的指针*/

struct ListNode* reverseList(struct ListNode* head){
    if (head == NULL)
       return head;
    struct ListNode* pre = NULL;
    struct ListNode* curr = head;
    struct ListNode* tmp = curr->next;
    while (curr != NULL){
        curr->next = pre; /*现在的指针倒转*/
        pre = curr;
        curr = tmp;
        if(tmp)
          tmp = tmp->next;
    }
    return pre;
}

法二:头插逆转

struct ListNode* reverseList(struct ListNode* head){
  struct ListNode* cur=head,*newnode=NULL;
     while(cur)
     {
         struct ListNode* next=cur->next;
        cur->next=newnode;
        newnode=cur;
        cur= next;
     }
     return newnode;
}

合并两个有序链表

法一:比较小的先尾插

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* newhead=NULL,*tail=NULL;
    if(list1==NULL)//存在一个链表为空,直接返回另一个链表
    return list2;
    if(list2==NULL)
    return list1;
    while(list1&&list2)
    {
        if(list1->val<=list2->val)
        {
            if(newhead==NULL)//第一次插入
            {
                newhead=tail=list1;
            }
            else
            {
                tail->next=list1;
                tail=tail->next;
            }
          list1=list1->next;//当成功插入完一个节点后,节点往后走一步
        }
        else
        {
            if(newhead==NULL)//第一次插入
            {
                newhead=tail=list2;
            }
            else
            {
                tail->next=list2;
                tail=tail->next;
            }
         list2=list2->next;
        }
    }
    if(list1==NULL)
    {
        while(list2)
        {
            tail->next=list2;
            tail=tail->next;
            list2=list2->next;
        }
    }
   if(list2==NULL)
    {
        while(list1)
        {
            tail->next=list1;
            tail=tail->next;
            list1=list1->next;
        }
    }
    return newhead;
}

链表分割

链表分割
在这里插入图片描述

#include <cstddef>
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x)
     {
        struct ListNode* lesshead,*lesstail,*greathead,*greattail;
        struct ListNode* cur=pHead;
           lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
           greathead=greattail=(struct ListNode*)malloc(sizeof(struct ListNode));
           while(cur)
           {
               if(cur->val<x)
               {
                  lesstail->next=cur;
                  lesstail=lesstail->next;
               }
               else
               {
                greattail->next=cur;
                greattail=greattail->next;
               }
                cur=cur->next;
           }
            lesstail->next=greathead->next;
             greattail->next=nullptr;
           pHead=lesshead->next;
           free(lesshead);
           free(greathead);
           return pHead;
    }
};

链表中的哨兵

在这里插入图片描述
head中我们一般不存放数据
head的功能在于我们引用head不需要担心head会是NULL,上题中我们就是运用哨兵的这一优点解决问题的。

链表的回文结构

链表的回文结构

在这里插入图片描述

struct ListNode* middleNode(struct ListNode* head){
      struct ListNode* slow=head,*fast=head;
      if(head==NULL)
      return NULL;
      while(fast!=NULL&&fast->next!=NULL)
      {
            slow=slow->next;
            fast=fast->next->next;
      }
      return slow;
}
struct ListNode* reverseList(struct ListNode* head){
    if (head == NULL)
       return head;
    struct ListNode* pre = NULL;
    struct ListNode* curr = head;
    struct ListNode* tmp = curr->next;
    while (curr != NULL){
        curr->next = pre; /*现在的指针倒转*/
        pre = curr;
        curr = tmp;
        if(tmp)
          tmp = tmp->next;
    }
    return pre;
}
class PalindromeList {
public:
    bool chkPalindrome(ListNode* head) {
        struct ListNode* mid=middleNode(head);//先找中点
        struct ListNode* rear=reverseList(mid);//再逆转后部
        while(rear)
        {
            if(head->val!=rear->val)
            return false;
            head=head->next;
            rear=rear->next;
        }
        return true;
    }
};

相交链表

相交链表
在这里插入图片描述

法一:遍历法(时间复杂度O(N^2))

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode * cura=headA,*curb=headB;
    while(cura)
    {
        curb=headB;
        while(curb)
        {
            if(cura==curb)
            return cura;
            curb=curb->next;
        }
        cura=cura->next;
    }
    return NULL;
}

示例代码:时间复杂度O(m+n)

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    //   struct ListNode*curA=headA;
    //   struct ListNode*curB=headB;
      struct ListNode*nA=headA;
      struct ListNode*nB=headB;
      int lenA=0,lenB=0;
      //这里不应该将判断条件设为nA和nB,因为虽然判断到最后一个节点,
      //但是下一步nA和nB都指向哪了不知道
      while(nA->next)
      {
          ++lenA;
          nA=nA->next;
      }
      while(nB->next)
      {
          ++lenB;
          nB=nB->next;
      }
      if(nA!=nB)
      {
          return NULL;
      }
        int len=abs(lenA-lenB);
        struct ListNode*longList=headA;
        struct ListNode*shortList=headB;
        if(lenB>lenA)
        {
            longList=headB;
            shortList=headA;
        }
        while(len--)
        {
            longList=longList->next;
        }
        while(longList!=shortList)
        {
            longList=longList->next;
            shortList=shortList->next;
        }
        return longList;
}

判断是否为环形链表

判断是否为环形链表

bool hasCycle(struct ListNode *head) {
    struct ListNode* slow=head,*fast=head;
    if(head==NULL)
    return false;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
         if(fast==slow)
        return true;
    }
    return false;
}

环形链表 II

环形链表 II
思路
在这里插入图片描述

struct ListNode *detectCycle(struct ListNode *head) {
     struct ListNode* slow=head,*fast=head;
    if(head==NULL)
    return NULL;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
         if(fast==slow)
        {
            struct ListNode* meet=fast;
            while(head!=meet)
            {
                head=head->next;
                meet=meet->next;
            }
            return meet;
        }
    }
    return NULL;
}

👉🏻双向链表

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
ListNode* CreatNewnode(Listtype x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->prev = NULL;
	newnode->next = NULL;
	newnode->data = x;
	return newnode;
}
ListNode* InitList()
{
	ListNode* phead= (ListNode*)malloc(sizeof(ListNode));
	phead->prev = phead;
	phead->next = phead;
	return phead;
}
void ListPushFront(ListNode* phead, Listtype x)
{
	assert(phead);
	ListNode* head = phead->next;
	ListNode* newnode = CreatNewnode(x);
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = head;
	head->prev = newnode;
}
void LTPopBack(ListNode* phead)
{
	assert(phead);
	ListNode* tail = phead->prev;
	ListNode* tailprev = tail->prev;
	tailprev->next = phead;
	phead->prev = tailprev;
	free(tail);
	tail = NULL;
}
void ListPushBack(ListNode* phead, Listtype x)
{
	ListNode* tail=phead->prev;
	ListNode* newnode = CreatNewnode(x);
	/*tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;*/
	tail->next = newnode;
	newnode->prev = tail;
	phead->prev = newnode;
	newnode->next = phead;
}

void LTPopFront(ListNode* phead)
{
	ListNode* head = phead->next;
	ListNode* headnext = head->next;
	phead->next = headnext;
	headnext->prev = phead;
	free(head);
}
ListNode* LTFind(ListNode* phead,Listtype x)
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;

}
void LTPosPush(ListNode* phead, ListNode* pos, Listtype x)
{
	assert(phead);
	ListNode* posprev = pos->prev;
	ListNode* newnode = CreatNewnode(x);
	posprev->next = newnode;
	newnode->prev = posprev;
	newnode->next = pos;
	pos->prev = newnode;
}
void LTPosErase(ListNode* phead, ListNode* pos)
{
	assert(phead);
	ListNode* posprev = pos->prev;
	ListNode* posnext = pos->next;
	posprev->next = posnext;
	posnext->prev = posprev;
	free(pos);
	pos = NULL;
}
void LTPrint(ListNode* phead)
{
	assert(phead);
	ListNode* cur = phead->next;
	printf("Guard<-->");
	while (cur != phead)
	{
		printf("%d<-->", cur->data);
		cur = cur->next;
	}
	printf("Guard");
}

SeqList.h

#pragma once
#include <stdio.h>
#include <assert.h>
typedef int Listtype;
typedef struct ListNode
{
	struct ListNode* prev;
	struct ListNode* next;
	Listtype data;
}ListNode;
ListNode* InitList();
void ListPushFront(ListNode* phead, Listtype x);//头插
void ListPushBack(ListNode* phead, Listtype x);//尾插
void LTPopFront(ListNode* phead);//头删
void LTPopBack(ListNode* phead);//尾删

ListNode* LTFind(ListNode* phead,Listtype x);//查找某一元素

void LTPosPush(ListNode* phead, ListNode* pos, Listtype x);//在某一位置进行插入
void LTPosErase(ListNode* phead, ListNode* pos);//在某一位置进行删除
ListNode* CreatNewnode(Listtype x);
void LTPrint(ListNode* phead);//打印

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话 ,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值