数据结构与算法每日一练---链表---0x0000

数据结构与算法每日一练---链表---0x0000

  • 移除链表元素
    • LeetCode题号:203
    • 所属类型:链表
    • 题目
      • 说明
      • 示例
      • 提示
    • 题解
      • 方法1:常规方法
      • 方法2:虚拟头节点
      • 方法3:采用C++ STL库处理

移除链表元素

LeetCode题号:203

所属类型:链表

题目

说明

给你一个链表的头节点head和一个整数val,请你删除链表中所有满足Node.val == val的节点,并返回新的头节点。

示例

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

输入:head = [], val = 1
输出:[]

输入:head = [7,7,7,7], val = 7
输出:[]

提示

  • 列表中的节点数目在范围 [0, 104] 内
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

题解

  • 链表节点
struct ListNode
{
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

方法1:常规方法

class Solution 
{
public:
    ListNode* removeElements(ListNode* head, int val)
    {
        ListNode* pPre = nullptr;
        ListNode* pCur = head;
        
        // 遍历链表,每个节点的值与目标值进行比对
        while (nullptr != pCur)
        {
            if (pCur->val == val)
            {
                pCur = DeleteNode(head, pPre, pCur);
            }
            else
            {
                pPre = pCur;
                pCur = pCur->next;
            }
        }

        return head;
    }

    /**
     *@brief 删除节点函数,并返回被删除节点的下一个节点
     */
    ListNode* DeleteNode(ListNode*& pHead, ListNode* pPre, ListNode* pDelete)
    {
        // 获取被删除节点的下一个节点
        ListNode* pNext = pDelete->next;

        // 当pPre为nullptr时,实则删除的节点就是头结点
        nullptr == pPre ? pHead = pNext : pPre->next = pNext;

        delete pDelete;
        return pNext;
    }
};
  • 方法分析:
    封装了一个删除节点的函数,函数的传入参数有三个
  1. ListNode*& pHead:头结点指针引用 ,目的是在删除节点为头结点时更新头结点
  2. ListNode* pPre :删除节点的前驱节点
  3. ListNode* pDelete:删除节点
    返回值为被删除节点的后一个节点

所以我们只需要对链表进行遍历,让每个节点的值域与目标值进行比对,相同则调用删除函数,不同则进行下一个节点的比对。

方法2:虚拟头节点

class Solution 
{
public:
    ListNode* removeElements(ListNode* head, int val)
    {
        // 创建一个虚拟头结点
        ListNode* pDummyHead = new ListNode(0, head);
        ListNode* pTemp = pDummyHead;

        // 采用 pTemp->next 的方式实际时处理的 pTemp 的下一个节点
        while (nullptr != pTemp->next)
        {
            if (pTemp->next->val == val)
            {
            	// 获取删除节点
                ListNode* pDelete = pTemp->next;
                // 将被删除的节点跳过
                pTemp->next = pTemp->next->next;
                delete pDelete;
            }
            else
            {
                pTemp = pTemp->next;
            }
        }

        head = pDummyHead->next;
        delete pDummyHead;

        return head;
    }
};
  • 方法分析:
    创建了一个虚拟的头节点pDummyHead,将遍历指针指向虚拟头节点,开始遍历整个链表。当pTemp指向pDummyHead时,我们处理的实际上是pTemp的后继节点,当pTemp的位置位于链表倒数第二个节点时,其next为最后一个节点,此时处理最后一个节点,当pTemp指向最后一个节点时,此时的后继节点为空,处理完成。这个方法的好处是不需要变量存储被删除节点的前驱节点。所有的节点都可以统一方式处理。

方法3:采用C++ STL库处理

class Solution 
{
public:
    ListNode* removeElements(ListNode* head, int val) 
    {  
        vector<ListNode*> vecTempNode;		// 不删除的节点
        vector<ListNode*> vecDeleteNode;	// 删除的节点

        ListNode* pCur = head;
        while (nullptr != pCur)
        {
            pCur->val == val ? vecDeleteNode.push_back(pCur) : vecTempNode.push_back(pCur);
            pCur = pCur->next;
        }
		
		// 释放删除的节点内存
        Release(vecDeleteNode);
		
		// 连接不删除的节点构成新链表
        head = LinkVecNode(vecTempNode);
        
        return head;
    }
	
	// 释放vector里面的节点
    void Release(vector<ListNode*>& vecNode)
    {
        if (vecNode.empty())
            return;

        for (size_t i = 0; i != vecNode.size(); ++i)
        {
            if (nullptr != vecNode[i])
                delete vecNode[i];    
        }    

        vecNode.clear();
    }
	
    ListNode* LinkVecNode(vector<ListNode*>& vecNode)
    {
        if (vecNode.empty())
            return nullptr;
		
		// 找到头节点
        ListNode* pHead = vecNode[0];
        ListNode* pTemp = pHead;

        for (size_t i = 1; i != vecNode.size(); ++i)
        {
        	// 方法一
            pTemp->next = vecNode[i];
			
			// 方法二:做一次判断,再看用不用赋值,因为有可能有些节点本身就是连接状态的
			/*
        	if (pTemp->next != vecNode[i])
            	pTemp->next = vecNode[i];
            */
            
            pTemp = pTemp->next;    
        }    
		// 此时pTemp指向的是最后一个节点
		// 一定要将其next置位空,否则next可能指向已经被删除的节点
        pTemp->next = nullptr;
        return pHead;
    }
};
  • 方法分析:
    采用了两个vector分别存储不用删除的节点和需要删除的节点,然后清空需要删除节点的内存,再链接不被删除的节点,从而构成题目所需的链表,要注意的是在链接不被删除的链表时,一定要将最后一个节点的next指针域值为空,否则可能出错,比如:
    【1, 2, 3, 4, 5】 目标删除值为5时,当进行每个节点的分类后,再链接不被删除的节点时最后一个节点是4,但此时它的next指针域并不为空,而是继续指向了被删除的节点5,所以需要手动将4的next置位空。

你可能感兴趣的:(数据结构与算法,链表,数据结构,算法,c++)