(尊重劳动成果,转载请注明出处: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. 用python的turtle画分形树

    由于分形树具有对称性,自相似性,所以我们可以用递归来完成绘制.只要确定开始树枝长.每层树枝的减短长度和树枝分叉的角度,我们就可以把分形树画出来啦!! 代码如下: # -*- coding: utf-8 ...

  2. HZNUOJ 2508:双峰插云

    我们注意到总的颜色种类只有200,故我们可以先处理出所有云朵中每一种颜色有多少朵云朵拥有,此处我们约定用$cnt[] 表示$ 然后暴力枚举将哪朵云分给老薛,记录三个临时变量$Max_{kk}, Max ...

  3. 【Flask】Flask上下文

    # 上下文: ### Local对象:在`Flask`中,类似于`request`的对象,其实是绑定到了一个`werkzeug.local.Local`对象上.这样,即使是同一个对象,那么在多个线程中 ...

  4. 重置root密码后仍然不能登陆

    一.忘记密码:二.输入正确用户名和密码时依旧无法登录. 一.忘记密码 进入单用户模式重置密码: 开机启动时,按‘E’键(倒计时结束前)进入界面 选择第二项,按‘E’键再次进入 在最后一行添加‘ 1’( ...

  5. iOS字符串处理

    拼接字符串 NSString* string; NSString* string1, string2; //方法1. string = [NSString initWithFormat:@" ...

  6. 剑指offer题解02-10

    02 单例模式 单例模式,是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例.即一个类只有一个对象实例. 从具体实现角 ...

  7. React Native实战系列教程之自定义原生UI组件和VideoView视频播放器开发

    React Native实战系列教程之自定义原生UI组件和VideoView视频播放器开发   2016/09/23 |  React Native技术文章 |  Sky丶清|  4 条评论 |  1 ...

  8. JAVA基础补漏--继承

    子类的对象在创建时,首先调用父类的构造函数,再调用子类自己的构造函数. 子类的构造函数中,有一个默认的super(),为一个无参调用,这个不显示,但会被首先调用,所有才会有父类构造函数被调用的情况. ...

  9. 简单的aop实现日志打印(切入点表达式)

    Spring中可以使用注解或XML文件配置的方式实现AOP. 1.导入jar包 com.springsource.net.sf.cglib -2.2.0.jar com.springsource.or ...

  10. MySQL 触发器简单实例 - 转载

    MySQL 触发器简单实例 触发器:可以更新,删除,插入触发器,不同种类的触发器可以存在于同一个表,但同种类的不能有多个.一个更新.一个删除是可以共存的. ~~语法~~ CREATE TRIGGER  ...