单链表的应用

1.移除链表元素

203. 移除链表元素 - 力扣(LeetCode)

看过题之后,我们可以想到两种方法。

思路一

我们可以依次遍历原链表,将原链表中结点值为val的结点依次释放掉。具体过程如下

我们使用pcur指针进行遍历原链表,用prev存储pcur的前一个结点,next存储pcur的后一个结点,若pcur->val == val ,则需要释放pcur,此时就需要next来存储pcur的下一个结点的地址。最后链表遍历完如图

思路二

创建一个新的链表,依次遍历原链表,找到结点值不为val的结点,依次尾插到新链表中。

具体过程如下:

从单链表的第一个结点开始遍历,如结点值不为val,则放入新的单链表中,pcur后移,newTail后移

当遇到结点值为val的时候,只有pcur后移,newTail不动,最后可以得到不含每一个结点值都不为val的新链表

按照这个思路,我们可以写出如下代码

运行过后,会出现最后一个原链表中值为val的结点为删除

解决:链表每个结点都有数据域和指针域组成,当5加入新链表中时,此时这个结点的指针域还是指向下一个结点,若下一个结点值为val就会造成最后一个结点值为val的结点为释放。

改成后如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
    //创建空链表
    ListNode* newHead,*newTail;
    newHead = newTail = NULL;
    //遍历原链表
    ListNode* pcur = head;
    while(pcur)
    {
        //找不为val的结点,尾插到新链表中
        if(pcur->val != val)
        {
            //链表为空
            if(newHead == NULL)
            {
                newHead = newTail = pcur;
            }
            //链表不为空
            else
            {
                newTail->next = pcur;
                newTail = newTail->next;
            }
        }
        pcur = pcur->next;
    }
    if(newTail)
        newTail->next = NULL;
    return newHead;
}

2.反转链表

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

思路一

我们可以直接反转原链表的指向,使之直接反转成功

具体过程如下图

代码实现如下

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* tail = NULL;
    struct ListNode* pcur = head;
    while(pcur)
    {
        struct ListNode* tmp = pcur->next;
        pcur->next = tail;
        tail = pcur;
        pcur = tmp;
    }
    return tail;
}

思路二 

使用三个指针完成反转

首先将n2指向n1,随后n1移到n2,n2移到n3,n3移到他的下一个结点,不断循环

最后,如图

代码实现如下

/**
 * 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* n1,*n2,*n3;
    n1 = NULL, n2 = head, n3 = n2->next;
    //开始循环
    while(n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if(n3)
            n3 = n3->next;
    }
    return n1;
}

3.链表的中间结点

876. 链表的中间结点 - 力扣(LeetCode)

思路一

最容易想到的就是先遍历一遍原链表得到链表的长度,之后再遍历到链表的中间结点即可

代码实现如下

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* p = head;
    int len = 0;
    int k = 0;
    while(p)
    {
        p = p -> next;
        len++;
    }
    p = head;
    while(k < len / 2)
    {
        k++;
        p = p -> next;
    }
    return p;
}

思路二   快慢指针

 这里使用快慢双指针可以更妙的解决此题

定义两个指针,一个为slow,一个为fast

slow每次走一步,fast每次走两步,结点数为单数时,当fast->next == NULL时,slow指向的刚好为中间结点

 结点数为偶数时 如图

代码实现如下

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head) {
    //定义快慢指针
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    //循环开始
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    //退出循环时,slow刚好指向中间结点
    return slow;
}

 4.合并两个有序链表

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

思路:创建一个新链表,遍历两个原链表,依次将结点值小的结点尾插到新链表中

代码实现如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    //判断是否为空链表
    if(list1 == NULL)
        return list2;
    if(list2 == NULL)
        return list1;
    ListNode* l1 = list1;
    ListNode* l2 = list2;

    //创建新链表
    ListNode* newHead,*newTail;
    newHead = newTail = NULL;

    while(l1 && l2)
    {
        if(l1->val < l2->val)
        {
            //尾插l1
            if(newHead == NULL)
            {
                newHead = newTail = l1;
            } else {
                newTail->next = l1;
                newTail = newTail->next;                
            }
            l1 = l1->next;
        }else {
            //尾插l2
            if(newHead == NULL)
            {
                newHead = newTail = l2;
            }else {
                newTail->next = l2;
                newTail = newTail->next;
            }
            l2 = l2->next;
        }
    }
    if(l1)
    {
        newTail->next = l1;
    }
    if(l2) 
    {
        newTail->next = l2;
    }
    return newHead;
}

观察代码发现,有很多重复且类似的代码,这是由于新链表存在空链表和非空链表两种情况,为了解决这个问题我们可以直接使新链表为非空链表,用malloc函数为其分配空间。

代码实现如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    //判断是否为空链表
    if(list1 == NULL)
        return list2;
    if(list2 == NULL)
        return list1;
    ListNode* l1 = list1;
    ListNode* l2 = list2;

    //创建新链表
    ListNode* newHead,*newTail;
    newHead = newTail = (ListNode*)malloc(sizeof(ListNode));

    while(l1 && l2)
    {
        if(l1->val < l2->val)
        {
            //尾插l1
            newTail->next = l1;
            newTail = newTail->next;
            l1 = l1->next;
        }else {
            //尾插l2
            newTail->next = l2;
            newTail = newTail->next;
            l2 = l2->next;
        }
    }
    if(l1)
    {
        newTail->next = l1;
    }
    if(l2) 
    {
        newTail->next = l2;
    }
    //释放内存
    ListNode* ret = newHead->next;
    free(newHead);
    newHead = NULL;
    return ret;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值