链表经典题目(上)以及双向链表的增删改查

 

 力扣链接

206. 反转链表 - 力扣(LeetCode)

自己的效率较低的思路,看看即可

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur=head;
    struct ListNode* tail=head;//追踪倒数第二个节点,快慢指针法
    struct ListNode* Newhead=head;
       if(head==NULL)
       {
          return head;
       }
       if(head->next==NULL)
       {
           return head;
       }
    while(cur->next!=NULL)
    {
        tail=cur;
        cur=cur->next;
    }
    tail->next=NULL;
    Newhead=cur;//记录新节点
    struct ListNode* CunNewhead=Newhead;//不让新节点改变
     while(head->next!=NULL)
     {
      cur=head;
       while(cur->next!=NULL)
       {
          tail=cur;
          cur=cur->next;
       }
       tail->next=NULL;
       CunNewhead->next=cur;
       CunNewhead=cur;
     }
     CunNewhead->next=head;
    return Newhead;
}

经典解法

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

 

 83. 删除排序链表中的重复元素 - 力扣(LeetCode)

struct ListNode* deleteDuplicates(struct ListNode* head){
    if(head==NULL) return head;
//仔细看题是已排序过的
        struct ListNode* slow=head;
        struct ListNode* fast=head->next;
        struct ListNode* tail;
        while(fast)//暴力解法(双指针),
        {//每次迭代时,慢指针跟快指针先比较
         //如果相等,快指针继续走
         //直到快指针走到NULL,因为快指针肯定先走完
           if(slow->val==fast->val)//如果相等,就把值相等的节点释放
           //然后重新链接
           {
               tail=fast;
               fast=fast->next;
               slow->next=fast;
               free(tail);
           }
           else//如果不相等快指针和满指针同时走一步
           {
               slow=slow->next;
               fast=fast->next;
           }
        }
        return head;
}

 

 

 21. 合并两个有序链表 - 力扣(LeetCode)

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    //含哨兵位的方法
    struct ListNode newnode;
    struct ListNode* head=&newnode;
    head->val=0;
    head->next=NULL;
    struct ListNode* tail=head;
    while(list1!=NULL && list2!=NULL)
    {
      if(list1->val>list2->val)//小的放下来,尾插到哨兵位后面
      {
         tail->next=list2;
         tail=tail->next;
         list2=list2->next;
      }
      else
      {
         tail->next=list1;
         tail=tail->next;
         list1=list1->next;
      }
    }
    if(list1==NULL)//注意情况可能有一边判断完了,另一边还没判断完
    {
       tail->next=list2;
    }
    else if(list2==NULL)
    {
        tail->next=list1;
    }
    return head->next;
}

 

 

struct Node* Buynewnode()//申请节点
{
    struct Node* ret =
        (struct Node*)malloc(sizeof(struct Node));
    if (ret == NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }
    ret->val = 0;
    ret->next = NULL;
    ret->random = NULL;
    return ret;
}
struct Node* copyRandomList(struct Node* head) {
    if(head == NULL) return head;//链表可能为空的情况
    struct Node* cur = head;
    struct Node* tail = head;
    struct Node* Copy = NULL;
    struct Node* nnnhead = head;
    while (cur)//每次在给定的链表后面复制一个相同的节点
    {
        Copy = Buynewnode();//每次复制时申请一个节点
        Copy->val = cur->val;//把此时位置的数值复制给新申请的节点
        Copy->next = cur->next;//先让申请的节点链接到旧链表此时位置的
        //下一个节点
        tail = cur->next;
        cur->next = Copy;//让旧的链表此时的位置的节点的下一个节点
        //链接到这个新节点
        cur = tail;
    }
    cur = head;
    tail = NULL;//追着cur->next看看tail是否在copy
    while (cur)//每次要把随机指针指向复制后的跟旧链表相似的节点
    {
        tail = cur->next;
        if (cur->random != NULL)
        {
            cur->next->random = cur->random->next;//因为当前指针的随机
            //指针的下一个节点就是在前面复制过后的节点
        }
        else
        {
            cur->next->random = cur->random;
        }
        cur = cur->next->next;
    }
    tail = head;//保存前一个节点释放节点,
    cur = head->next;
    struct Node* newhead = head->next;
    while (cur && cur->next )//分离复制后的链表,将复制后的
    //链表重新链接
    {
       
        tail = cur->next;
        cur->next = cur->next->next;
        cur = cur->next;       
    }
    return newhead;
}

 


 

struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
    struct ListNode* c1 = headA;
    struct ListNode* c2 = headB;
    int L1 = 0;
    int L2 = 0;
    while (c1)//判断相差的值
    {
        c1 = c1->next;
        ++L1;
    }
    while (c2)//判断相差的值
    {
        c2 = c2->next;
        ++L2;
    }
    int count = abs(L2 - L1);
    //判断长短
    struct ListNode* Short = headA;//假设法
    struct ListNode* Long = headB;
    if (L2 > L1)//判断长短,重新赋值
    {

    }
    else
    {
        Long = headA;
        Short = headB;
    }
    while (count--)//长的先走
    {
        Long = Long->next;
    }
    while (Short != Long)//等短的和长的走到一起时停止,我们相交
    //之后链表后面的节点数量是一样的,而单链表相交只能指向一个地址
    //不能指向两个地址
    {
        Long = Long->next;
        Short = Short->next;
        if (Short == NULL)
        {
            return NULL;
        }
    }
    return Short;
}

 


带头双向链表

#pragma once
// 带头+双向+链表增删查改实现
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

// 创建返回链表的头结点.
ListNode* ListCreate();
ListNode* ListCreate()
{
	ListNode* head = (ListNode*)malloc(sizeof(ListNode));
	if (head == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	head->_next=head->_prev = NULL;
	head->_data = -1;//哨兵位
	return head;
}
// 双向链表销毁
void ListDestory(ListNode* pHead);
void ListDestory(ListNode* pHead)
{
    //这里不用判断是否为空的情况,我们的哨兵位不可能为空
   //因为如果为空,我们在创建哨兵位时就已经退出并报错了
	ListNode* cur = pHead->_next;
	ListNode* tail = pHead->_next;
	while (cur)
	{
		tail = cur;
		cur = cur->_next;
		free(tail);
	}
	pHead->_next = NULL;
	pHead->_prev = NULL;
}
// 双向链表打印
void ListPrint(ListNode* pHead);
void ListPrint(ListNode* pHead)
{
	ListNode* cur = pHead->_next;//不要打印哨兵位的数据
	while (cur)
	{
		printf("%d->", cur->_data);
		cur = cur->_next;
	}
	printf("NULL\n");
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);//尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	ListNode* tail = pHead;//尾插的指针
	ListNode* prev = pHead;//因为要链接前一个,每次遍历时,需要记录上一个节点
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));//申请新节点
//链接到尾节点
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->_next = NULL;
	newnode->_prev = NULL;
	newnode->_data = x;//初始化
	while (tail)
	{
		prev = tail;//为什么每次要记录tail经过的上一个节点
        //因为我们这里是双向链表尾插的时候要链接前一个节点
        //相当于我们是在两个节点中插入,
        //左边是正常节点,右边是空,所以根据我的这个思路就用prev
        //并且因为我们没用追随节点的话,tail走到空,
        //当我们在插入的时候,
		tail = tail->_next;
	}
	tail = newnode;
	prev->_next = tail;
	newnode->_prev = prev;	
}
// 双向链表尾删
void ListPopBack(ListNode* pHead);//尾删
void ListPopBack(ListNode* pHead)
{
	ListNode* tail = pHead;
	//ListNode* prev = pHead;//记录前一个节点,给他的next置为空
	while (tail->_next)
	{
		/*prev = tail;*/
		tail = tail->_next;
	}
	if (tail == pHead)//没有结点的情况
	{
		;
	}
	else
	{
		tail->_prev ->_next = NULL;
		free(tail);
		/*prev->_next = NULL;*/
	}
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);//头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	ListNode* head = pHead;//哨兵位
	ListNode* cur = pHead->_next;
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));//申请新节点
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->_next = NULL;
	newnode->_prev = NULL;
	newnode->_data = x;//初始化


	newnode->_next = head->_next;//将新节点的next指向哨兵位的下一个节点
	newnode->_prev = head;//将新节点的位置的prev指向哨兵位
	head->_next = newnode;//哨兵位的next指向新节点
	if (newnode->_next == NULL)//空节点
    //因为我们如果是在两个节点中插入的话,当第二个节点为空时,
    //我们就不能让他指向前一个节点
	{
		;
	}
	else
	{
		newnode->_next->_prev = newnode;//第二个节点不为空
	}
}
// 双向链表头删
void ListPopFront(ListNode* pHead);
void ListPopFront(ListNode* pHead)
{
	ListNode* cur = pHead->_next;
	ListNode* tail = cur;
	if (cur)//如果哨兵位后面此时节点为空时,就不要去释放
    //会造成释放空指针
	{
      
	}
	else if (cur->_next == NULL)//如果节点为1的话我们不用重新链接释放过后的节点
	{
		pHead->_next = cur->_next;//让他们指向空就行
		free(cur);
	}
	else//节点不为1和0的情况
	{
		pHead->_next = cur->_next;
		cur->_next->_prev = pHead;
		free(cur);
	}
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	ListNode* cur = pHead->_next;
	while (cur)
	{
		if (cur->_data == x)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->_next = NULL;
	newnode->_prev = NULL;
	newnode->_data = x;

   //链接前后两个节点,因为pos找不到的话(空节点也包括在内)会我们在前面就会判断并退出,所以我们不需要去判断有木有节点
	newnode->_prev = pos->_prev; //将新节点的prev指向pos位置的前一个节点
	newnode->_next = pos;//将新节点的next指向pos,
	pos->_prev->_next = newnode;//将pos指向的前一个节点的next指向新节点
	pos->_prev = newnode;//将pos的prev指向新节点
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
void ListErase(ListNode* pos)
{
	assert(pos);
	if (pos->_next == NULL)//如果pos的位置是在最后一个的话,我们就不需要去链接
    //新节点,那是为什么呢,我们看第二个条件
	{
		pos->_prev->_next = NULL;
		free(pos);
	}
	else
	{
		pos->_prev->_next = pos->_next;
		pos->_next->_prev = pos->_prev;//如果最后一个节点的nxt指向空,那么
        //pos->_next->_prev就对空指针解引用了
		 free(pos);
	}
}

带头双向循环链表

//带头双向循环链表
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;


// 创建返回链表的头结点.//这个不用改
ListNode* ListCreate();
ListNode* ListCreate()
{
	ListNode* head = (ListNode*)malloc(sizeof(ListNode));
	if (head == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	head->_next = head->_prev = head;
	head->_data = -1;//哨兵位
	return head;
}
// 双向链表销毁
void ListDestory(ListNode* pHead);
void ListDestory(ListNode* pHead)//不用改
{
	ListNode* cur = pHead->_next;
	ListNode* tail = pHead->_next;
	while (cur && cur != pHead)
	{
		tail = cur;
		cur = cur->_next;
		free(tail);
	}
	pHead->_next = pHead;
	pHead->_prev = pHead;
}
// 双向链表打印
void ListPrint(ListNode* pHead);
void ListPrint(ListNode* pHead)//
{
	ListNode* cur = pHead->_next;
	while (cur && cur != pHead)//我们停止条件是cur指向哨兵位时
	{
		printf("%d->", cur->_data);
		cur = cur->_next;
	}
	printf("NULL\n");
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);//尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
    //因为是带头循环的双向链表,我们在初始化时不需要去判断节点是否为空
	ListNode* tail = pHead;//尾插的指针
	ListNode* prev = pHead;//因为要链接前一个,每次遍历时,需要记录上一个节点
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->_next = NULL;
	newnode->_prev = NULL;
	newnode->_data = x;

    //对比双向链表,我们的链表为空时,哨兵位的next和prev都指向的是自己
    //尾插过程
	newnode->_prev = pHead->_prev;
	newnode->_prev->_next = newnode;
	newnode->_next = pHead;
	pHead->_prev = newnode;
}
// 双向循环链表尾删
void ListPopBack(ListNode* pHead);//尾删
void ListPopBack(ListNode* pHead)
{
    //不用判断链表节点是否为空
	pHead->_prev->_prev->_next = pHead;//因为我们的哨兵位的prev
    //指向的时尾节点
	ListNode* cun = pHead->_prev;
	pHead->_prev = pHead->_prev->_prev;
	if (cun != pHead)
	{
		free(cun);
	}
}
// 双向循环链表头插
void ListPushFront(ListNode* pHead, LTDataType x);//头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	ListNode* head = pHead;//头插的指针
	ListNode* cur = pHead->_next;
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->_next = NULL;
	newnode->_prev = NULL;
	newnode->_data = x;
    
    //头插的过程
	newnode->_next =pHead->_next;
	pHead->_next->_prev = newnode;
	pHead->_next = newnode;
	newnode->_prev = pHead;
}
// 双向链表头删
void ListPopFront(ListNode* pHead);
void ListPopFront(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->_next != pHead); 
	ListNode* cur = pHead;

    //记住如果不用cur,那我们得理清他的先后顺序
   //因为顺序错了,可能节点就找不到了
	cur = pHead->_next;
	pHead->_next = pHead->_next->_next;
	pHead->_next->_prev = pHead;
	free(cur);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	ListNode* cur = pHead->_next;
	while (cur && cur != pHead)
	{
		if (cur->_data == x)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
void ListInsert(ListNode* pos, LTDataType x)//不用改
{
	assert(pos);
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->_next = NULL;
	newnode->_prev = NULL;
	newnode->_data = x;




	newnode->_prev = pos->_prev;
	newnode->_next = pos;
	pos->_prev->_next = newnode;
	pos->_prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
void ListErase(ListNode* pos)
{
	assert(pos);
	ListNode* cun = pos;
	pos->_prev->_next = pos->_next;
	pos->_next->_prev = pos->_prev;
	free(cun);
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乖的小肥羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值