网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
指定位置后删除
我们还是删除指定位置后的元素,因为如果删除指定元素,我们也需要它的前一个节点。单链表无法直接获取前一个节点。指定位置后删除,我们只需要保存下一个节点的下一个节点,然后删除下一个节点,然后让这个位置指向保存下来的的节点。
单链表的代码实现:
#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode\* next;
}SListNode;
// 动态申请一个节点
SListNode\* BuySListNode(SLTDateType x)
{
SListNode\* newNode = (SListNode\*)malloc(sizeof(SListNode)); //开辟空间
newNode->data = x; //空间值为x
return newNode;
}
// 单链表打印
void SListPrint(SListNode\* plist)
{
SListNode\* cur = plist;
while (cur) //遍历一遍链表
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
// 单链表尾插
void SListPushBack(SListNode\*\* pplist, SLTDateType x)
{
assert(pplist);
if (\*pplist == NULL)//如果链表为空调用头插
{
SListPushFront(pplist, x);
return;
}
//找到尾部节点
SListNode\* tail = \*pplist;
while (tail->next)
{
tail = tail->next;
}
//复用SListInsertAfter函数插入
SListInsertAfter(tail, x);
}
// 单链表的头插
void SListPushFront(SListNode\*\* pplist, SLTDateType x)
{
assert(pplist);
SListNode\* newNode = BuySListNode(x);
if (\*pplist == NULL) //链表为空需要更新头节点
{
\*pplist = newNode;
newNode->next = NULL;
return;
}
newNode->next = \*pplist;
\*pplist = newNode;
}
// 单链表的尾删
void SListPopBack(SListNode\*\* pplist)
{
assert(pplist && \*pplist);
if ((\*pplist)->next == NULL) //链表就一个元素,调用头删
{
SListPopFront(pplist);
return;
}
SListNode\* tail = \*pplist;
SListNode\* prev = NULL;
//遍历链表并删除
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
}
// 单链表头删
void SListPopFront(SListNode\*\* pplist)
{
assert(pplist && \*pplist);
//头删
SListNode\* next = (\*pplist)->next;
free(\*pplist);
\*pplist = next;
}
// 单链表查找
SListNode\* SListFind(SListNode\* plist, SLTDateType x)
{
SListNode\* cur = plist;
while (cur)
{
if (x == cur->data)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode\* pos, SLTDateType x)
{
assert(pos);
SListNode\* next = pos->next;
SListNode\* newNode = BuySListNode(x);
pos->next = newNode;
newNode->next = next;
}
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode\* pos)
{
assert(pos);
SListNode\* nextnext = pos->next->next;
free(pos->next);
pos->next = nextnext;
}
// 单链表的销毁
void SListDestroy(SListNode\* plist)
{
SListNode\* cur = plist;
SListNode\* next = plist->next;
while (cur)
{
next = cur->next;
free(cur);
cur = next;
}
}
双链表
这里我们实现一个带头循环的双链表,因为循环链表可以从头节点直接找到最后一个节点。带头是因为更好的确定链表的尾部,带头节点在这里就冲当了尾节点的作用。
指定位置前插入
双链表我们只需要写指定插入和指定删除,就可以复用这两个接口实现头插,头删,尾插,尾删了。指定位置前插入和单链表类似。先保存前一个节点坐标,然后让前一个节点和新节点连接,再把自己的prev指针指向新节点。
为了方便观看,我把首尾节点指向的线省略了,实际上指针是指向首尾的。我们在pos前插入一个节点。
动图演示可能线连接的不怎么好看,但最后都是这样的
指定位置删除
指定位置删除,我们只需要存下前一个节点和后一个节点。然后把两个节点连接起来,再释放当前节点即可。
双链表代码实现:
#include <assert.h>
#include<stdio.h>
// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct \_ListNode
{
LTDataType _data;
struct \_ListNode\* _next; //指向后一个节点
struct \_ListNode\* _prev; //指向前一个节点
}ListNode;
//创建节点
ListNode\* CreateListNode(LTDataType x)
{
ListNode\* newNode = (ListNode\*)malloc(sizeof(ListNode));
newNode->_data = x;
return newNode;
}
// 创建链表,返回链表的头结点.
ListNode\* ListCreate()
{
ListNode\* head = CreateListNode(0);
head->_next = head; //自己指向自己
head->_prev = head; //自己指向自己
return head;
}
// 双向链表销毁
void ListDestory(ListNode\* pHead)
{
ListNode\* cur = pHead->_next;
while (cur != pHead)
{
ListNode\* next = cur->_next;
free(cur);
cur = next;
}
free(pHead);
}
// 双向链表打印
void ListPrint(ListNode\* pHead)
{
ListNode\* cur = pHead->_next;
while(cur != pHead)
{
printf("%d->", cur->_data);
cur = cur->_next;
}
printf("NULL\n");
}
// 双向链表尾插
void ListPushBack(ListNode\* pHead, LTDataType x)
{
ListInsert(pHead, x); //复用指定插入
}
// 双向链表尾删
void ListPopBack(ListNode\* pHead)
{
ListErase(pHead->_prev); //复用指定删除
}
// 双向链表头插
void ListPushFront(ListNode\* pHead, LTDataType x)
{
ListInsert(pHead->_next, x); //复用指定插入
}
// 双向链表头删
void ListPopFront(ListNode\* pHead)
{
ListErase(pHead->_next); //复用指定删除
}
// 双向链表查找
ListNode\* ListFind(ListNode\* pHead, LTDataType x)
{
while (pHead)
{
if (pHead->_data == x)
return pHead;
pHead = pHead->_next;
}
return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode\* pos, LTDataType x)
{
ListNode\* prev = pos->_prev;
ListNode\* newNode = CreateListNode(x);
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = pos;
pos->_prev = newNode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode\* pos)
{
assert(pos);
ListNode\* prev = pos->_prev;
ListNode\* next = pos->_next;
free(pos);
prev->_next = next;
next->_prev = prev;
}
数组模拟单链表
数组模拟链表我们需要开一个数组来存链表的值,还要开一个数组连存储链表的指向,还需要一个变量来代表链表的编号。
e[] 数组用来存储链表的值 , ne[] 数组用来存储节点指向节点的下标,数组的下标代表节点。 head代表头节点,值-1则为空节点。 idx 是给链表的节点的一个编号,代表下标。
代码:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
代码:
[外链图片转存中…(img-61KgMdmp-1715715844281)]
[外链图片转存中…(img-210cNxto-1715715844281)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!