移除链表元素

  • 题目
  • 函数原型
  • 边界判断
  • 算法设计:哨兵
  • 算法设计:递归



 


题目

删除链表中等于给定值 [203].移除链表元素_递归

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

 


函数原型

C 的函数原型:

struct ListNode* removeElements(struct ListNode* head, int val){
    
}

 


边界判断

struct ListNode* removeElements(struct ListNode* head, int val){
    
}

 


算法设计:哨兵

思路:好的结构设计,可以简化算法。按照常规的删除方法,释放很多元素都适用,唯独删除第一个元素,需要特殊处理。更好的方法,是采用哨兵实现通用化。

如果删除的节点是中间的节点,则问题似乎非常简单:

  • 选择要删除节点的前一个结点 [203].移除链表元素_递归_02
  • [203].移除链表元素_递归_02[203].移除链表元素_算法设计_04 设置为要删除结点的 [203].移除链表元素_算法设计_04

[203].移除链表元素_递归_06


当要删除的一个或多个节点位于链表的头部时,事情会变得复杂。

[203].移除链表元素_链表_07


可以通过哨兵节点去解决它,哨兵节点广泛应用于树和链表中,如伪头、伪尾、标记等,它们是纯功能的,通常不保存任何数据,其主要目的是使链表标准化,如使链表永不为空、永不无头、简化插入和删除。

[203].移除链表元素_递归_08


在这里哨兵节点将被用于伪头。

typedef struct ListNode Node;
struct ListNode* removeElements(struct ListNode* head, int val){
    // 创建哨兵结点
    Node* sentinel = malloc( sizeof(Node) );
    sentinel->next = head;     // 哨兵 -> 头指针 

    Node* cur = sentinel;
    while( cur->next != NULL ){
        if( cur->next->val == val ){    // 找到了,就删除结点
            Node* del_node = cur->next;
            cur->next = del_node->next;
            free(del_node), del_node = NULL;
        }else
            cur = cur->next;
    }

    Node* ans = sentinel->next;
    free(sentinel), sentinel = NULL;

    return ans;   
}
  • 时间复杂度:[203].移除链表元素_链表_09
  • 空间复杂度:[203].移除链表元素_递归_10
     

算法设计:递归

思路:链表的增、删、改、查都可以用递归实现,这个也是。

struct ListNode* removeElements(struct ListNode* head, int val){
    if( head == NULL )
        return NULL;
    
    //2、递去:直到到达链表尾部才开始删除重复元素
    head->next = removeElements(head->next, val);
    
    //3、递归式:相等就是删除head,不相等就不用删除
    return head->val==val ? head->next : head;
}
  • 时间复杂度:[203].移除链表元素_链表_09
  • 空间复杂度:[203].移除链表元素_链表_09