Java单链表面试题(新浪. 百度. 腾讯)

本文详细介绍了单链表的相关操作,包括计算有效节点数量、查找倒数第k个节点、链表反转及从尾到头打印链表。采用Java实现,涉及链表遍历、栈的应用以及链表结构的修改。通过这些实例,深入理解链表数据结构及其在面试中的常见问题。
摘要由CSDN通过智能技术生成

单链表面试题(新浪. 百度. 腾讯)

  1. 求单链表中有效节点的个数

        // 测试一下 求单链表中有效节点的个数
            System.out.println("有效的节点个数=" + getLength(singleLinkedListD.getHead()));
        }
    
        /**
         *
         * @param head 链表的头节点
         * @return 返回的就是有效节点的个数
         */
        // 方法: 获取到单链表的节点的个数 (如果是带头节点的链表, 需要不统计头节点)
        public static int getLength(HeroNode head) {
            if (head.next == null) {
                // 空链表
                return 0;
            }
            int length = 0;
            //定义一个辅助的变量
            HeroNode cur = head.next;
            while(cur != null) {
                length++;
                cur = cur.next; // 遍历
            }
            return length;
        }
    }
    
  2. 查找单链表中的倒数第k个结点 [新浪面试题]

    思路:
    1. 编写一个方法, 接收head节点, 同时接收一个index
    2. index 表示的是倒数第index个节点
    3. 先把链表从头到尾遍历, 得到链表的总长度getLength
    4. 得到size后, 我们从链表的第一个开始遍历(size - index) 个,就可以得到
    5. 如果找到了, 则返回该节点, 否则返回null
          //测试一下, 看看是否得到了倒数第k个节点
            HeroNode res = findLastIndexNode(singleLinkedListD.getHead(), 2);
            System.out.println("res=" + res);
        }
    
    
        //    查找单链表中的倒数第k个结点[新浪面试题]
        public static HeroNode findLastIndexNode(HeroNode head, int index) {
            // 判断如果链表为空, 返回null
            if (head.next == null) {
                return null; // 没有找到
    
            }
            // 第一个遍历得到链表的长度 (节点个数)
            int size = getLength(head);
            // 第二次遍历size - index 位置, 就是我们倒数的第k个节点
            // 先做一个index的校验
            if (index <= 0 || index > size) {
                return null;
            }
            // 定义当前辅助变量, for循环定位到倒数的index
            HeroNode cur = head.next;
            for (int i = 0; i < size-index; i++) {
                cur = cur.next;
            }
            return cur;
        }
    
  3. 单链表的反转 [腾讯面试题,有点难度]

    思路:

    1. 先定义一个节点 reverseHead = new HeroNode();
    2. 从头到尾遍历原来的链表, 每遍历一个节点, 就将其取出, 并放在新的链表reverseHead 的最前端
    3. 原来的链表的head.next = reverseHead.next
          //测试一下, 单链表的反转功能
            System.out.println("原来链表的情况~~");
            singleLinkedListD.list();
    
            System.out.println("反转单链表~~");
            reversetList(singleLinkedListD.getHead());
            singleLinkedListD.list();
        }
    
        // 将单链表反转
        public static void reversetList(HeroNode head) {
            //如果当前链表为空, 或者只有一个节点, 无需反转, 直接返回
            if (head.next == null || head.next.next == null) {
                return;
            }
            // 定义一个辅助的指针(变量), 帮助我们遍历原来的链接
            HeroNode cur = head.next;
            HeroNode next = null; //指向当前下一个节点
            HeroNode reverseHead = new HeroNode(0,"","");
            //遍历原来的链表,
            // 并从头到尾遍历原来的链表, 每遍历一个节点, 就将其取出, 并放在新的链表reverseHead 的最前端
            while (cur != null) {
                next = cur.next; // 先暂时保存当前的下一个节点,因为后面需要使用
                cur.next = reverseHead.next; // 将cur的下一个节点指向新的链表的最前端
                reverseHead.next = cur; // 将cur连接到新的链表上
                cur = next; //让cur后移
            }
            // 将head.next 指向reverseHead.next, 实现单链表的反转
            head.next = reverseHead.next;
        }
    
    
  4. 从尾到头打印单链表[百度: 要求方式 1: 反向遍历,2. Stack栈]

    思路:

    1. 逆序打印单链表
    2. 方式一: 先将单链表进行反转操作, 然后再遍历即可, 这样做的问题是会破坏原来的单链表的结构, 不建议
    3. 方式二: 可以利用栈这个数据结构, 将各个节点压入到栈中,利用栈的先进后出的特点, 就实现了逆序打印的效果

    演示栈的使用Stack

    package DataStructures;
    
    import java.util.Stack;
    
    public class TestStack {
        public static void main(String[] args) {
            Stack<String> stack = new Stack();
            // 入栈
            stack.add("Li");
            stack.add("wen");
            stack.add("hui");
    //        System.out.println(stack.pop() ); // 取出之后就没有了
            //出栈
            while (stack.size() > 0) {
                System.out.print(stack.pop() + " "); // pop就是将栈顶的数据取出
            }
        }
    }
    

        // 测试逆序打印单链表,没有改变链表的结构
        System.out.println("测试逆序打印单链表,没有改变链表的结构");
        reversePrint(singleLinkedListD.getHead());

    }
//    方式二: 可以利用栈这个数据结构, 将各个节点压入到栈中,利用栈的先进后出的特点, 就实现了逆序打印的效果
    public static void reversePrint(HeroNode head) {
        if (head.next == null) {
            return; // 空链表 无法打印
        }
        //创建要给一个栈, 将各个节点压入栈
        Stack<HeroNode> stack = new Stack<HeroNode>();
        HeroNode cur = head.next;
        // 将链表的各个节点压入栈
        while (cur != null) {
            stack.push(cur);
            cur = cur.next; // cur后移, 这样就可以压入下一个节点
        }
        // 将栈中的节点进行打印, pop出栈
        while(stack.size() > 0) {
            System.out.println(stack.pop());// stack特点是先进后出
        }
    }

全部代码

package DataStructures;

import java.util.Stack;

public class SingleLinkedList {
    public static void main(String[] args) {
        // 进行测试
        // 先创建节点
        HeroNode hero1 = new HeroNode(1, "宋宋", "江江");
        HeroNode hero2 = new HeroNode(2, "文文", "雨雨");
        HeroNode hero3 = new HeroNode(3, "哈哈", "嘿嘿");
        HeroNode hero4 = new HeroNode(4, "依依", "落落");
        // 创建要给链表
        SingleLinkedListD singleLinkedListD = new SingleLinkedListD();
        // 加入
//        singleLinkedListD.add(hero1);
//        singleLinkedListD.add(hero2);
//        singleLinkedListD.add(hero3);
//        singleLinkedListD.add(hero4);

        // 加入按照编号的顺序
        singleLinkedListD.addByOrder(hero1);
        singleLinkedListD.addByOrder(hero2);
        singleLinkedListD.addByOrder(hero3);
        singleLinkedListD.addByOrder(hero4);
        singleLinkedListD.addByOrder(hero3);
        //显示
        singleLinkedListD.list();

        HeroNode newHeroNode = new HeroNode(2, "文", "雨~~");
        singleLinkedListD.update(newHeroNode);
        System.out.println("修改后的链表情况");
        singleLinkedListD.list();

        // 删除一个节点
        singleLinkedListD.del(1);
        System.out.println("删除后的链表情况");
        singleLinkedListD.list();

        // 测试一下 求单链表中有效节点的个数
        System.out.println("有效的节点个数=" + getLength(singleLinkedListD.getHead()));

        //测试一下, 看看是否得到了倒数第k个节点
        HeroNode res = findLastIndexNode(singleLinkedListD.getHead(), 2);
        System.out.println("res=" + res);

        //测试一下, 单链表的反转功能
        System.out.println("原来链表的情况~~");
        singleLinkedListD.list();

        System.out.println("反转单链表~~");
        reversetList(singleLinkedListD.getHead());
        singleLinkedListD.list();

        // 测试逆序打印单链表,没有改变链表的结构
        System.out.println("测试逆序打印单链表,没有改变链表的结构");
        reversePrint(singleLinkedListD.getHead());

    }
//    方式二: 可以利用栈这个数据结构, 将各个节点压入到栈中,利用栈的先进后出的特点, 就实现了逆序打印的效果
    public static void reversePrint(HeroNode head) {
        if (head.next == null) {
            return; // 空链表 无法打印
        }
        //创建要给一个栈, 将各个节点压入栈
        Stack<HeroNode> stack = new Stack<HeroNode>();
        HeroNode cur = head.next;
        // 将链表的各个节点压入栈
        while (cur != null) {
            stack.push(cur);
            cur = cur.next; // cur后移, 这样就可以压入下一个节点
        }
        // 将栈中的节点进行打印, pop出栈
        while(stack.size() > 0) {
            System.out.println(stack.pop());// stack特点是先进后出
        }
    }

    // 将单链表反转
    public static void reversetList(HeroNode head) {
        //如果当前链表为空, 或者只有一个节点, 无需反转, 直接返回
        if (head.next == null || head.next.next == null) {
            return;
        }
        // 定义一个辅助的指针(变量), 帮助我们遍历原来的链接
        HeroNode cur = head.next;
        HeroNode next = null; //指向当前下一个节点
        HeroNode reverseHead = new HeroNode(0,"","");
        //遍历原来的链表,
        // 并从头到尾遍历原来的链表, 每遍历一个节点, 就将其取出, 并放在新的链表reverseHead 的最前端
        while (cur != null) {
            next = cur.next; // 先暂时保存当前的下一个节点,因为后面需要使用
            cur.next = reverseHead.next; // 将cur的下一个节点指向新的链表的最前端
            reverseHead.next = cur; // 将cur连接到新的链表上
            cur = next; //让cur后移
        }
        // 将head.next 指向reverseHead.next, 实现单链表的反转
        head.next = reverseHead.next;
    }

    //    查找单链表中的倒数第k个结点[新浪面试题]
    //思路:
    // 1. 编写一个方法, 接收head节点, 同时接收一个index
    // 2. index 表示的是倒数第index个节点
    // 3. 先把链表从头到尾遍历, 得到链表的总长度getLength
    // 4. 得到size后, 我们从链表的第一个开始遍历(size - index) 个,就可以得到
    // 5. 如果找到了, 则返回该节点, 否则返回null
    public static HeroNode findLastIndexNode(HeroNode head, int index) {
        // 判断如果链表为空, 返回null
        if (head.next == null) {
            return null; // 没有找到

        }
        // 第一个遍历得到链表的长度 (节点个数)
        int size = getLength(head);
        // 第二次遍历size - index 位置, 就是我们倒数的第k个节点
        // 先做一个index的校验
        if (index <= 0 || index > size) {
            return null;
        }
        // 定义当前辅助变量, for循环定位到倒数的index
        HeroNode cur = head.next;
        for (int i = 0; i < size-index; i++) {
            cur = cur.next;
        }
        return cur;
    }
    /**
     *
     * @param head 链表的头节点
     * @return 返回的就是有效节点的个数
     */
    // 方法: 获取到单链表的节点的个数 (如果是带头节点的链表, 需要不统计头节点)
    public static int getLength(HeroNode head) {
        if (head.next == null) {
            // 空链表
            return 0;
        }
        int length = 0;
        //定义一个辅助的变量
        HeroNode cur = head.next;
        while(cur != null) {
            length++;
            cur = cur.next; // 遍历
        }
        return length;
    }
}

// 定义SingleLinked 管理我们的英雄
class SingleLinkedListD {
    //先初始化一个头节点, 头节点不要动,
    private HeroNode head = new HeroNode(0,"","");

    // 返回头节点
    public HeroNode getHead() {
        return head;

    }


    public void add(HeroNode heroNode) {
        //因为head节点不能动, 因此我们需要一个辅助变量temp
        HeroNode temp = head;
        // 遍历链表, 找到最后
        while(true) {
            // 找到链表的最后
            if (temp.next == null) {
                break;
            }
//            如果没有找到最后,就将temp后移
            temp = temp.next;
        }
        // 当退出while循环时, temp就指向了链表的最后
        // 将最后这个节点的next 指向 新的节点
        temp.next = heroNode;
    }

//    第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
      public void addByOrder (HeroNode heroNode) {
        // 因为头节点不能动, 因此我们仍然通过一个辅助指针(变量) 来帮助找到添加的位置
          //因为单链表, 因此我们找的temp 是位于添加位置的前一个节点, 否则插入不了
          HeroNode temp = head;
          boolean flag = false; // flag标志添加的编号是否存在, 默认为false
          while(true) {
              if (temp.next == null) { // 说明temp已经在链表的最后
                  break;//

              }
              if (temp.next.no > heroNode.no) { // 位置找到, 就在temp 的后面插入
                  break;

              } else if (temp.next.no == heroNode.no) {
                  // 说明希望添加的heroNode的编号已然存在
                  flag = true; // 说明编号存在
                  break;
              }
              temp = temp.next; // 后移, 遍历当前链表

          }
          // 判断flag 的值
          if (flag) { // 不能添加,说明编号存在
              System.out.println("准备插入的英雄编号%d已经存在了, 不能加入\n" + heroNode.no);
          } else {
              // 插入到链表中 temp的后面
              heroNode.next = temp.next;
              temp.next = heroNode;
          }
      }
// 修改节点的信息, 根据no的编号来修改, 即no编号不能改
    //说明
    // 1. 根据newHeroNode 的no来修改即可
    public void update(HeroNode newHeroNode) {
        // 判断是否空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        // 根据需要修改的节点, 根据no编写
        // 定义一个辅助变量
        HeroNode temp = head.next;
        boolean flag = false; // 表示是否找到该节点
        while(true) {
            if (temp == null) {
                break;// 已经遍历完链表
            }
            if (temp.no == newHeroNode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag 判断是否找到要修改的节点
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        } else { // 没有找到
            System.out.println("没有找到编号为%d 的节点, 不能修改\n" + newHeroNode.no);
        }
    }

    //删除节点
    //思路
    //1.head不能动 因此我们需要temp辅助节点找到待删除节点的前一个节点
    // 2. 说明我们在比较时,时temp.next.no和需要删除的节点的no比较
    public void del(int no) {
        HeroNode temp = head;
        boolean flag = false; // 标志是否找到待删除节点的
        while (true) {
            if (temp.next == null) { //已经到链表的最后
                break;
            }
            if (temp.next.no == no) {
                // 找到了待删除的前一个节点temp
                flag = true;
                break;
            }
            temp = temp.next;// temp后移 遍历
        }
        //判断flag
        if (flag) { // 找到
            // 可以删除
            temp.next = temp.next.next;
        }else {
            System.out.println("要删除的%d 节点不存在\n" + no);
        }
    }
    //显示链表(遍历)
    public void list() {
        // 判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        // 因为头节点, 不能动 因此我们需要一个辅助变量来遍历
        HeroNode temp = head.next;
        while(true) {
            // 判断是否到链表最后
            if (temp == null) {
                break;
            }
            // 输出节点信息
            System.out.println(temp);
            // 将temp后移, 一定小心
            temp = temp.next;
        }
    }
}
// 定义HeroNode , 每个HeroNode 对象就是一个节点
class HeroNode {
    public int no;
    public String name;
    public String nickname;
    public HeroNode next; // 指向下一个节点

    // 构造器
    public HeroNode(int no, String hName, String hNickname) {
        this.no = no;
        this.name = hName;
        this.nickname = hNickname;
    }
    // 为了显示方法, 我们重新toString
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值