(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76061004冷血之心的博客)

关于单链表反转的多种形式请参见本博文:

多种单链表反转面试题总结

本文总结常见面试题中关于删除和去除节点的相关问题。题目如下:

1、给出一单链表头指针pHead和一节点指针pToBeDeleted,O(1)时间复杂度删除节点pToBeDeleted: delete

2、去除重复节点,保留一个即可

3、去除重复节点,将重复节点全部删除

4、删除具有指定val值的节点

5、删除倒数第N个节点

我们先来看看第一题:

(1)给出一单链表头指针pHead和一节点指针pToBeDeleted,O(1)时间复杂度删除节点pToBeDeleted: delete

思路:通过复制下一个节点的val值到要删除的节点,而把下一个节点删除,修改了next指针,实现了O( 1 )复杂度删除指定节点。(该题的假设是要删除的节点一定存在该链表中,否则O(1)不可能实现)

/**
     * 给出一单链表头指针head和一节点指针toBeDeleted,O(1)时间复杂度删除节点tBeDeleted
     * 对于删除节点,我们普通的思路就是让该节点的前一个节点指向该节点的下一个节点
     * ,这种情况需要遍历找到该节点的前一个节点,时间复杂度为O(n)。对于链表,
     * 链表中的每个节点结构都是一样的,所以我们可以把该节点的下一个节点的数据复制到该节点
     * ,然后删除下一个节点即可。要注意最后一个节点的情况,这个时候只能用常见的方法来操作,先找到前一个节点,但总体的平均时间复杂度还是O(1)
     */
    public static void delete(Node head, Node toDelete){
        if(toDelete == null){
            return;
        }
        if(toDelete.next != null){          // 要删除的是一个中间节点
            toDelete.val = toDelete.next.val;       // 将下一个节点的数据复制到本节点!
            toDelete.next = toDelete.next.next;
        }
        else{       // 要删除的是最后一个节点!
            if(head == toDelete){       // 链表中只有一个节点的情况
                head = null;
            }else{
                Node node = head;
                while(node.next != toDelete){   // 找到倒数第二个节点
                    node = node.next;
                }
                node.next = null;
            }
        }
    }    

第二题:

(2)、去除重复节点,保留一个即可

思路:该题最后返回的结果要求保留一个重复的节点,这就比较easy了,直接判断,跳过与当前节点重复的节点即可。

public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
       if(head==null||head.next==null)
			return head;
		ListNode cur = head;
		while(cur.next!=null){
			if(cur.val==cur.next.val)
				cur.next = cur.next.next;
			else
				cur = cur.next;
		}
		return head;
    }
}

第三题:(Hard)

(3)、去除重复节点,将重复节点全部删除

思路:将重复节点全部删除,一个不留,这就是本题的难点所在,我们必须保存当前节点的前一个节点,因为我们不知道当前节点是否是重复节点。

还有一个难点,当开头就是重复元素时,我们需要调整head,这时候需要一些判断条件。

public class Solution {
    public ListNode deleteDuplication(ListNode pHead){
		if(pHead==null||pHead.next==null)
            return pHead;
        ListNode cur = pHead;
        ListNode preNode = null;
        while(cur!=null){
            if(cur.next!=null&&cur.val==cur.next.val){
                int val = cur.val;
                // 跳过重复节点
                while(cur.next!=null&&cur.next.val==val)
                    cur = cur.next;
                // 若开头即是重复元素,则更新pHead
                if(preNode==null)
                    pHead = cur.next;
                else   // 反之更新preNode
                    preNode.next = cur.next;

            }else{
                preNode = cur;
            }
            cur = cur.next;
        }
        return pHead;
    }
}

该代码中如下部分:

// 若开头即是重复元素,则更新pHead
                if(preNode==null)
                    pHead = cur.next;
                else   // 反之更新preNode
                    preNode.next = cur.next;

将preNode和pHead刚开始指向同一个节点,之后将不再移动pHead,通过preNode的移动,将新的链表穿起来~

第四题:

(4)、删除具有指定val值的节点

思路:这个也不难,首先我们判断该节点是不是需要删除的节点,如果是的话,借助题目1中删除该节点的方法来搞定。判断该节点是中间节点?还是最后一个节点?如果是最后一个节点,则需要从前到后遍历链表。注意只有一个节点的情况,即要删除的节点是头结点同时也是尾节点。

public class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head==null)
            return null;
        if(head.next==null&&head.val==val)
            return null;
        if(head.next==null&&head.val!=val)
            return head;

        ListNode cur = head;
        while(cur!=null){
            // 如果是准备删除的元素
            if(cur.val==val){
                if(cur.next!=null){
                	// 要删除的不是最后一个元素
                    cur.val = cur.next.val;
                    if(cur.next.next!=null)
                        cur.next = cur.next.next;
                    else
                        cur.next = null;
                    // 直接continue
                    continue;
                }else{
                	// 要删除的是最后一个元素
                   ListNode node = head;
                   // 防止出现即是第一个也是最后一个几点的情况
                   if(node==cur)
                	   return null;
                   while(node.next!=cur){
                	   node = node.next;
                   }
                   node.next = null;
                }
            }
        	cur = cur.next;
        }
        return head;
    }
}

第五题:

(5)、删除倒数第N个节点

思路:链表中遇到倒数N,K 啥的问题一般都是双指针来搞定。本题,设立两个指针,一个先走N步之后,两个指针同时走,这样当前面的指针走到最后时,后面的指针走到了要删除的倒数第N个节点,我们调整该节点的指向,删除该节点即可。

public class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head==null)
            return null;
        ListNode slow = head;
        ListNode fast = head;
        while(n>0&&fast!=null){
            fast = fast.next;
            n--;
        }
        if(fast==null)
        	return head.next;
        while(fast.next!=null){
            slow = slow.next;
            fast = fast.next;
        }
        slow.next = slow.next.next;
        return head;
    }
}

如果对你有帮助,记得点赞哦~欢迎大家关注我的博客,可以进群366533258一起交流学习哦~

本群给大家提供一个学习交流的平台,内设菜鸟Java管理员一枚、精通算法的金牌讲师一枚、Android管理员一枚、蓝牙BlueTooth管理员一枚、Web前端管理一枚以及C#管理一枚。欢迎大家进来交流技术。

单链表删除(Delete)或者去除(Remove)节点面试题总结的更多相关文章

  1. 单链表 C++ 实现 - 含虚拟头节点

    本文例程下载链接:ListDemo 链表 vs 数组 链表和数组的最大区别在于链表不支持随机访问,不能像数组那样对任意一个(索引)位置的元素进行访问,而需要从头节点开始,一个一个往后访问直到查找到目标 ...

  2. 以K个为一组反转单链表,最后不足K个节点的部分也反转

    package StackMin.ReverseList_offer16; public class ReverseKgroup_extend_offer16 { /** * 分组反转单链表,最后不足 ...

  3. 20140719 找到单链表的倒数第K个节点 判断一个链表是否成为一个环形 反转

    1.找到单链表的倒数第K个节点 2.判断一个单链表对否形成环形 3.单链表翻转

  4. C语言实现单链表(不带头结点)节点的插入

    对单链表进行增删改查是最主要的操作.我在上一篇博客<C语言实现链表节点的删除>实现了删除单链表中的某个节点. 这里我们要来实如今某个位置插入节点.演示样例代码上传至https://gith ...

  5. Leetcode24--->Swap Nodes in Pairs(交换单链表中相邻的两个节点)

    题目:给定一个单链表,交换两个相邻的节点,且返回交换之后的头节点 举例: Given 1->2->3->4, you should return the list as 2-> ...

  6. iOS常用算法之单链表查找倒数第n个节点(图解)

    拿到题目, 首先要先了解链表数据结构, 如下图: 常规思路: 利用数组, 遍历整个单链表, 将每个节点装入数组中, 最终拿到数组根据索引(数组长度-1-n)就得到了倒数第n个元素, 这里要注意从数组中 ...

  7. 面试题-----求单链表的倒数第k个节点

    #include <iostream> using namespace std; struct node{ int value; struct node *next; }; struct ...

  8. C++循环单链表删除连续相邻重复值

    比如:1(头)->2->2->3->3->1->1(头) 去除以后的结果是1->2->3,注意头尾的1也要去掉一个. #include "st ...

  9. C++获取单链表的倒数第k个节点

    /* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) { } };*/ c ...

随机推荐

  1. iOS学习之数据持久化详解

    前言 持久存储是一种非易失性存储,在重启设备时也不会丢失数据.Cocoa框架提供了几种数据持久化机制: 1)属性列表: 2)对象归档: 3)iOS的嵌入式关系数据库SQLite3: 4)Core Da ...

  2. beego——过滤器

    beego支持自定义过滤中间件,例如安全验证.强制跳转等. 过滤器函数如下所示: beego.InsertFilter(pattern string, position int, filter Fil ...

  3. CNN学习笔记:激活函数

    CNN学习笔记:激活函数 激活函数 激活函数又称非线性映射,顾名思义,激活函数的引入是为了增加整个网络的表达能力(即非线性).若干线性操作层的堆叠仍然只能起到线性映射的作用,无法形成复杂的函数.常用的 ...

  4. Tomcat 源码分析(转)

    本文转自:http://blog.csdn.net/haitao111313/article/category/1179996 Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件 ...

  5. java调用操作系统命令

    java的Runtime.getRuntime().exec(commandStr)可以调用执行cmd指令. cmd /c dir 是执行完dir命令后关闭命令窗口. cmd /k dir 是执行完d ...

  6. D3学习之动画和变换

    D3学习之动画和变换 ##(17.02.27-02.28) 主要学习到了D3对动画和缓动函数的一些应用,结合前面的选择器.监听事件.自定义插值器等,拓展了动画的效果和样式. 主要内容 单元素动画 多元 ...

  7. Introspector内省和反射的区别.

    Introspector 是一个专门处理bean的工具类.用来获取Bean体系里的 propertiesDescriptor,methodDescriptor. 要理解这个,就要理解下面几个议题.   ...

  8. dotnet new vue [C# 使用 vuejs]

    1. 安装 dotnet sdk 2.0 2. 安装 nodejs , npm 3. 安装淘宝镜像 4. 更新npm :   npm update -g 5. dotnet new -i vue 6. ...

  9. 正则表达式【TLCL】

    grep[global regular expression print] print lines matching a pattern grep [options] regex [file...] ...

  10. ubuntu 14.04 建立wifi热点

    昨天突然想起来我可以用笔记本搞一个热点这样我的手机就不用上流量了,但是手机死活搜不到建好的信号,目前的解决方案如下: 直接用ubuntu自带的创建wifi网络功能是不好使的,因为android系统不支 ...