博主个人主页:不是笨小孩
⏩专栏分类:数据结构与算法
代码仓库:笨小孩的代码库
⏩社区:不是笨小孩
欢迎大家三连关注,一起学习,一起进步!!
我们可以遍历一遍链表,然后将等于val的值的节点删除了,然后在将剩余的链接起来即可。我们就需要两个指针,一个遍历链表另一个记录上一个节点的位置,如果相等就删除链接,等到链表遍历结束,就可以得到新的链表,但是这种放大有一个弊端,就是如果头结点就是val我们无法处理,所以我们需要现将头结点是val的情况处理了,头结点是val,我们只需要头删即可。
代码如下:
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* prev = NULL,*cur = head;
while(cur)
{
if(cur->val==val)
{
//相等就要删除
if(cur==head)
{
//如果头结点是就相等,就要头删
head = cur->next;
free(cur);
cur = head;
}
else
{
//正常删除
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else
{
// 不相等prev记录上一个节点,cur往下走
prev = cur;
cur = cur->next;
}
}
return head;
}
我们可以依旧是遍历原链表,但是我们这一次需要一个新的头,然后与val不相等的我们尾插到新的头中就可以了,这个方法比较省事,直接尾插就可以,但是需要注意新的头为空的情况,为了方便尾插,我们需要一个尾指针来记录尾节点的位置。
代码如下:
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* cur = head,*tail=NULL,*newhead=NULL;
while(cur)
{
if(cur->val==val)
{
//相等就删除
struct ListNode* next = cur->next;
free(cur);
cur = next;
}
else
{
//不相等就尾插
if(newhead==NULL)
{
//第一次尾插
newhead = tail = cur;
cur= cur->next;
}
else
{
//正常尾插
tail->next= cur;
cur = cur->next;
tail = tail->next;
}
}
}
//尾差的最有后一个节点的next要置NULL,不然可能会非法访问
if(tail!=NULL)
//tail等于空说明链表为空,或者链表中的元素都val
tail->next = NULL;
return newhead;
}
我们定义一个空指针,然后翻转链表的指向,但是直接翻转的话我们会丢失下一个节点,所以我们还需要一个指针来记录下一个节点的位置。
代码如下:
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* newhead = NULL,*cur = head,*next = NULL;
while(cur)
{
//next指向下一个节点
next = cur->next;
//翻转指向
cur->next = newhead;
//更新新的头结点
newhead = cur;
//更新cur
cur = next;
}
return newhead;
}
头插法,我们遍历原链表,然后把节点都头插到新的头处,即可翻转链表。
代码如下:
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* newhead = NULL,*cur = head;
while(cur)
{
//保存下一个节点
struct ListNode* next = cur->next;
//头插
cur->next = newhead;
newhead = cur;
cur = next;
}
return newhead;
}
这个题使用快慢指针就可以解决。我们让慢指针一次走一步,快指针一次走两步,我们就可以知道当快指针为空时,或者当快指针的next为空时此时的慢指针就是中间节点。
struct ListNode* middleNode(struct ListNode* head){
struct ListNode* slow = head,*fast = head;
while(fast&&fast->next)
{
//慢指针走一步
slow=slow->next;
//快指针走两步
fast = fast->next->next;
}
return slow;
}
这个题同样使用快慢指针,我们可以让快指针先走k步,然后两个指针一起走,当快指针为空时,慢指针就是倒数第k个节点,如果先让快指针走k-1步的话,当快指针的next为空时,慢指针就是倒数第k个节点。这两种方法都可以。我们实现走k步的
代码如下:
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
struct ListNode* fast = pListHead,*slow = pListHead;
while(k--)
{
// fast=NULL前K个就为空了(判断k是不是过大,以及链表是不是空链表)
if(fast==NULL) return NULL;
fast = fast->next;
}
while(fast)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
这个题我们可以同时遍历两个链表,然后拿小的下来尾插,思想差不多就是这样,我们需要一个头,但是这个头可以是哨兵位,也可以是空,我们这里写两个版本的。
带哨兵位的
代码如下:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
//如果一个链表为空直接返回另一个链表
if(list1==NULL) return list2;
if(list2==NULL) return list1;
//创建哨兵位
struct ListNode* newhead = NULL,*tail = NULL;
newhead = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
while(list1&&list2)
{
if(list1->val<list2->val)
{
//不用考虑头结点为空的情况直接尾插就可以
tail->next = list1;
tail = tail->next;
list1= list1->next;
}
else
{
//不用考虑头结点为空的情况直接尾插就可以
tail->next = list2;
tail = tail->next;
list2= list2->next;
}
}
//如果一个链表为空,直接将另一个链表链接过来
if(list1==NULL) tail->next = list2;
if(list2==NULL) tail->next = list1;
//保返回的头
struct ListNode* next = newhead->next;
//释放哨兵位
free(newhead);
return next;
}
不带哨兵位
代码如下:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
//如果一个链表为空直接返回另一个链表
if(list1==NULL) return list2;
if(list2==NULL) return list1;
struct ListNode* newhead = NULL,*tail = NULL;
while(list1&&list2)
{
if(list1->val<list2->val)
{
//判断头结点是否为空
if(newhead==NULL)
{
newhead = tail = list1;
list1 = list1->next;
}
else
{
tail->next = list1;
tail = tail->next;
list1= list1->next;
}
}
else
{
//判断头结点是否为空
if(newhead==NULL)
{
newhead = tail = list2;
list2 = list2->next;
}
else
{
tail->next = list2;
tail = tail->next;
list2= list2->next;
}
}
}
//如果一个链表为空,直接将另一个链表链接过来
if(list1==NULL) tail->next = list2;
if(list2==NULL) tail->next = list1;
return newhead;
}
今天的分享就到这里了,感谢大家的支持和关注。