单链表:
我们上节课学了顺序表,接下来让我们看看顺序表的问题
首先我们来扩展一个知识点
顺序表支持连续存储空间,不进行分期还内存,eg:若在一个顺序表中我们要free一个数据,这是不可以的,因为顺序表是连续的,不可以部分的进行还内存
优势是支持下标访问
链表如果想要支持分期付款,也就是可以free一个数据,为了还清贷款,一共10000块,我们可以申请10次,每次1000,每次还一次的贷款时,不影响其他的次数还贷款
其次,我们先看一个代码,要明白其中的奥秘
进行画图理解:
大家都知道,链表分为有节点和无节点
首先我们来看简单的部分,
有节点:大家注意pphead不管何时,都需要断言
尾插法
尾插法的思路就是首先要去找尾,然后新建一个节点,最后将节点插入在NULL前面
//找尾
void SLNBackNode(SLNode*phead,SNDataType x)
{
//最好不要动头结点
SLNode* tail=phead;
while(tail->next!=NULL)
{
tail=tail->next;
}
}
//新建一个新节点并赋值
void CreatNode(SLNode*phead,SNDataType x)
{
SLNode*newNode=(SLNode*)malloc(sizeof(SLNode);
{
if(newNode==NULL)
{
perror(“newNode fail);
exit(-1);
}
newNode->val=x;
newNode->next=NULL;
}
return newNode;
}
代码实现:
void CreateNode(SLNode* phead, SLNDataType x)
{
//链表不为空
SLNode* newNode = (SLNode*)malloc(sizeof(SLNode));
while (newNode == NULL)
{
perror("malloc fail");
exit(-1);
}
newNode->val = x;
newNode->next = NULL;
return newNode;
}
void SLNodePushBack(SLNode** phead, SLNodeDataType x)
{
if(*phead=NULL)
{
*pphead=newNode;
}
SLNode* tail = phead;
while (tail->next != NULL)
{
tail = tail->next;
}
SLNode* newNode = CreateNode(x);
tail->next = newNode;
}
让我们看看代码中的易错点
头插法:
头插法比较简单(因为它同时处理了链表是否为空的情况)
尾删法:
尾删法分为两种情况,如图下所示
思路:
当我们释放掉一个空间,会出现野指针的情况,因此,为了解决野指针的问题,我们设置两个指针,一个是prev,一个是tail;prev在tail指针的前面,当tail->next是NULL,prev指向了d2,这时,释放d3空间,将prev指向NULL
代码实现:
头删法:
和尾删法一样,分为单节点和多节点的情况
好啦~这些操作我们今天就讲这些,后面会把剩下的操作讲好
现在让我们看一道关于链表的习题吧
例题1:
思路:
我们可以设置两个变量,一个是slow(走一步),另一个是fast(走两步),fast是slow的二倍,当fast走到NULL时,slow就是那个中间节点
代码实现:
例题2:
思路:
首先我们要先设置三个指针,分别是prev(指针的前一个节点),cur(指针当前节点),next(当前节点的下一个节点),当cur不等于NULL时,有两种情况,一个是cur等于重复的值val,此时我们要将prev->cur,然后释放掉空间,最后将prev->next=next;第二种情况是,若cur不等于重复的值val,此时我们要将prev->cur,cur=cur->next
代码实现:
找到某个值:
void SLNodeFind(SLNode* phead, SLNDataType x)
{
SLNode* cur = phead;
if (cur->val == x)
{
return cur;
}
else
{
cur = cur->next;
}
在pos位置的前一个插入节点:
void SLNodegml(SLNode** phead, SLNode* pos, SLNDataType x)
{
if (*pphead == pos)
{
SLNode* newnode = SLNodeCreate(x);
newnode->next = *pphead;
*phead = newnode;
}
else
{
SLnode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLNode* newnode = SLNodeCreate(x);
newnode->next = pos;
prev->next = newnode;
}
}
删除pos位置节点:
void gmll(SLNode** phead, SLNode* pos, SLNDataType x)
{
if (*pphead == pos)
{
SLNode* next = (*pphead)->next;
*pphead = next;
free(*pphead);
}
else
{
SLNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev -> next = pop->next;
free(pos);
pos = NULL;
}
}
无指针,在pos位置的后面插入:
void SLNodeInsertAfter(SLNode** phead, SLNode* pos)
{
SLNode* newnode = CreateNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
给大家一个挑战,倘若没有头指针,我们该怎么在pos的位置前面插入呢?
这里博主给个思路
只有pos指针,在pos位置的后面删除:
void SLTEraseAfter(SLNode* pos)
{
assert(pos);
SLNode* tmp = pos->next;
pos->next = pos->next->next;
free(tmp);
tmp = NULL;
}
毁坏一个单链表
void STDestory(SLNode** pphead)
{
SLNode* cur = *phead;
while (cur)
{
SLNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
这里让我们了解一下哨兵位
例题1:
代码实现:
例题2:反转链表:
代码实现1:(箭头变方向)
struct reverseList* (struct ListNode* head)
{
if (head == NULL)
{
return NULL;
}
struct reverseList* n1, * n2, * n3;
n1 = NULL;
n2 = head;
n3 = n2->next;
while (n2)
{
n2->next = n1;
n1 = n2;
if (n3)
{
n3 = n3->next;
}
}
}
代码实现2:(头插法)
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* cur = head;
struct ListNode* newhead = NULL;
while (cur)
{
struct ListNode* next = head->next;
cur->next = newhead;
newhead = cur;
cur = next;
}
return newhead;
}
例题3:
思路:
代码实现:
struct ListNode*Find(struct ListNode* head, int k)
{
struct ListNode* slow = head, * fast = head;
while (k--)
{
if (fast == NULL)
return -1;
fast = fast->next;
}
while (fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
例题4:
代码实现:
例题5 :
思路,代码实现:
问题+答案:
总结:
第三个问题:证明
最后,本题的代码实现:
例题6:
思路:同例题1的思路一样,尾插小的节点
代码实现:
struct ListNode* mergeTwoLists(struct ListNode* list1,struct ListNode* list2)
{
struct ListNode* tail = NULL, * head = NULL;
if (list1 == NULL)
return list2;
if (list2 == NULL)
return list1;
while (list1 && list2)
{
if (list1->val < list2->val)
{
if (tail == NULL)
{
tail = list1 = head;
}
else
{
tail->next = list1;
tail = tail->next;
}
list = list->next;
}
else
{
if (tail == NULL)
{
tail = head = list2;
}
else
{
tail->next = list2;
tail = tail->next;
}
list2 = list2->next;
}
}
}
例题7:回文序列
思路:
先找出中间值,然后进行逆置,将逆置的节点放入另一个表中,然后和原表进行比对,如果数值正确的话,就是回文序列,否则就false
代码实现:
bool chkPalindrome(listNode* head)
{
struct ListNode* mid = middleList(head);
struct ListNode* rhead = reverseList(mid);
while (head && rhead)
{
if (head->val != rhead->val)
{
return -1;
}
else
{
head = head->next;
rhead = rhead->next;
}
return true;
}
}
例题9:
思路+代码实现:
好啦~单链表的知识和例题讲解就到这里了,希望大家可以多多支持哟