链表的相关面试题

链表的相关面试题

定义一个节点类,为后面的操作做准备

package com.datestructures.linkedlist;

public class HeroNode {
    //定义节点
    //这里以水浒传为例
    public int no;//排名
    public String name;
    public String nickname;
    public HeroNode next;

    public HeroNode(int no, String name, String nickname) {
        this.no=no;
        this.name=name;
        this.nickname=nickname;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

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

思路

这是一道比较简单的面试题,当我们拿到这道题的时候,先知道什么是有效节点,通过观察定义的节点,我们知道当一个节点不等于null就是有效节点,我们定义一个length,当我们遇到有效节点就length++;

完整代码如下

 /**
     * 用来统计链表中有效节点的个数,不包括头结点
     *
     * @param heroNode 链表的头结点
     * @return 返回的就是有效头结点的个数
     */
    public int getLength(HeroNode heroNode) {
        //如果头结点的next的为null,说明这个链表是空链表
        if (heroNode.next == null) {
            return 0;
        }
        HeroNode temp = heroNode.next;
        int length = 0;
        while (temp != null) {
            length++;
            temp = temp.next;
        }
        return length;
    }

2.查找单链表中的倒数第k个结点

思路

1.编写一个方法,接收一个整数n

2.n表示是倒数第几个节点

3.先把节点从头遍历一遍,得到链表的有效元素的个数

4.得到size()后,我们从链表的第一个开始遍历,遍历(length-n)个,就可以得到想要的节点

5.如果找到了就返回该节点,没有找到就返回null

完整代码如下

/**
     * 先统计链表中的有效个数,然后再次遍历到length-index的位置
     *
     * @param n 查找倒数的第index个节点
     * @return 返回找到的节点或null
     */
    public HeroNode getHeroNode(int n) {
        //如果为空,返回null
        if (headNode.next == null) {
            return null;
        }
        //第一次遍历得到链表中的有效个数
        int sum = getLength(headNode);
        if (sum >= n || 0 <= n) {
            HeroNode temp = headNode.next;
            for (int i = 0; i < sum - n; i++) {
                temp = temp.next;
            }
            return temp;
        }
        return null;
    }

3.单链表的反转

思路

1.先创建一个reversalHeadNode

2.将原链表的第一个元素摘下来

3.将摘下来的元素放到reversalHeadNode

4.重复第二步操作

5.将摘下来的元素再次插入到reversalHeadNode后边

6.以此反复

7.最后将headNode指向反转链表

完整代码如下

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

4.从尾到头打印单链表

思路

1.第一种方式: 将原始链表反转后遍历,这种方式会破坏原始链表的结构,不建议

2.第二种方式: 用栈的特性来辅助完成操作 栈是一种先进后出的数据结构

public void reversalPrint(HeroNode headNode) {
        //如果链表为空或者链表只有一个元素则直接返回
        if (headNode.next == null | headNode.next.next == null) {
            return;
        }
        //创建一个栈
        Stack<HeroNode> stack = new Stack<>();
        //定义一个辅助指针
        HeroNode temp = headNode.next;
        //将链表中的元素压入栈中
        while (temp != null) {
            stack.add(temp);
            temp = temp.next;
        }
        //出栈
        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }

5.合并两个有序的单链表,合并之后的链表依然有序

思路

1.创建一个新的链表用于存放合并的两边元素

2.用链表中的addByOrder()方法分别遍历两个链表

3.因为这是一个有序插入方法,所以直接调用后就可以得出新的有序链表

完整代码如下

public SingleLinkedList mergeList(HeroNode headNode0, HeroNode headNode1) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        if (headNode0.next == null & headNode1.next == null) {
            return null;
        }
        HeroNode temp = headNode0;
        HeroNode next = null;
        while (temp!=null) {
            next = temp.next;
            singleLinkedList.addByOrder(temp);
            temp = next;

        }
        temp = headNode1;
        while (temp!=null) {
            next = temp.next;
            singleLinkedList.addByOrder(temp);
            temp = next;

        }
        return singleLinkedList;
    }
//添加方式,为顺序添加,根据hero.no的排名添加
    //如果链表中有这个节点,那么添加失败
    public void addByOrder(HeroNode heroNode) {
        //头节点不能动 定义一个辅助指针
        HeroNode temp = headNode;
        //定义一个flag 用来判断链表中是否含有这个值
        boolean flag = false;
        while (true) {
            //当temp==null时,就遍历到链表的最后位置还没有找到加入的位置,就加入到最后
            if (temp.next == null) {
                break;
            }
            //当temp.next.no>heroNode.no时,就找到位置,在temp后边
            if (temp.next.no > heroNode.no) {
                break;
            } else if (temp.next.no == heroNode.no) {//当temp.next.no==heroNode.no时,说明链表中有该值,则flag=true;
                flag = true;
            }
            temp = temp.next;
        }
        if (flag) {
            System.out.println(heroNode.no + "该值存在,不能再次加入!");
            return;
        }
        heroNode.next = temp.next;
        temp.next = heroNode;
    }

完整代码

SingleLinkedList类

package com.datestructures.linkedlist;

import java.util.Stack;

//单向链表
public class SingleLinkedList {
    //初始化链表
    //创建一个头结点
    private HeroNode headNode = new HeroNode(0, "", "");

    //返回头结点
    public HeroNode getHeadNode() {
        return headNode;
    }


    //添加元素到链表,头结点不能动
    //第一种添加英雄的方式 不按顺序,往链表尾部添加
    public void add(HeroNode heroNode) {
        //头结点不能动 所以定义一个辅助节点用来遍历链表
        HeroNode temp = headNode;
        //先找到链表尾,从尾部加入新的节点,所以先遍历
        while (true) {
            //当temp==null的时候,就找到最后节点的位置
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        //当跳出循环后,就说明找到了链表尾部
        temp.next = heroNode;
    }

    //第二种添加方式,为顺序添加,根据hero.no的排名添加
    //如果链表中有这个节点,那么添加失败
    public void addByOrder(HeroNode heroNode) {
        //头节点不能动 定义一个辅助指针
        HeroNode temp = headNode;
        //定义一个flag 用来判断链表中是否含有这个值
        boolean flag = false;
        while (true) {
            //当temp==null时,就遍历到链表的最后位置还没有找到加入的位置,就加入到最后
            if (temp.next == null) {
                break;
            }
            //当temp.next.no>heroNode.no时,就找到位置,在temp后边
            if (temp.next.no > heroNode.no) {
                break;
            } else if (temp.next.no == heroNode.no) {//当temp.next.no==heroNode.no时,说明链表中有该值,则flag=true;
                flag = true;
            }
            temp = temp.next;
        }
        if (flag) {
            System.out.println(heroNode.no + "该值存在,不能再次加入!");
            return;
        }
        heroNode.next = temp.next;
        temp.next = heroNode;
    }

    //修改节点信息
    //以no为查询条件,进行修改
    public void update(HeroNode newHeroNode) {
        //先判断是否为空
        if (headNode.next == null) {
            System.out.println("链表为空,不能修改!");
            return;
        }
        //头结点不能动,定义一个辅助指针
        HeroNode temp = headNode.next;
        //定义一个flag,用来判断是否找到该节点
        boolean flag = false;
        while (true) {
            //如果temp==null 说明找到链表尾部还没有找到
            if (temp == null) {
                System.out.println("该链表没有该元素!");
                break;
            } else if (temp.no == newHeroNode.no) {
                //temp.no==newHeroNode.no  说明找到了
                flag = true;
                break;
            }
            temp = temp.next;//后移操作
        }
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        }
    }

    //删除指定no的节点
    //思路: 先找到要删除节点的前一个元素
    //再将 temp.next=temp.next.next  temp.next就没有引用指向它,那么他就会被jvm回收
    public void delete(int no) {
        //先判断是否为空
        if (headNode.next == null) {
            return;
        }
        //头结点不能动,定义一个辅助指针
        HeroNode temp = headNode;
        //定义一个flag 用来判断链表中是否含有这个值
        boolean flag = false;
        while (true) {
            //如果temp==null 说明找到链表尾部还没有找到
            if (temp.next == null) {
                break;
            } else if (temp.next.no == no) {
                flag = true;
                break;
            }
            temp = temp.next;//后移
        }
        if (flag) {
            temp.next = temp.next.next;
        }
    }

    /**
     * 用来统计链表中有效节点的个数,不包括头结点
     *
     * @param heroNode 链表的头结点
     * @return 返回的就是有效头结点的个数
     */
    public int getLength(HeroNode heroNode) {
        //如果头结点的next的为null,说明这个链表是空链表
        if (heroNode.next == null) {
            return 0;
        }
        HeroNode temp = heroNode.next;
        int length = 0;
        while (temp != null) {
            length++;
            temp = temp.next;
        }
        return length;
    }

    //查找单链表中的倒数第k个结点 【新浪面试题】

    /**
     * 先统计链表中的有效个数,然后再次遍历到length-index的位置
     *
     * @param n 查找倒数的第index个节点
     * @return 返回找到的节点或null
     */
    public HeroNode getHeroNode(int n) {
        //如果为空,返回null
        if (headNode.next == null) {
            return null;
        }
        //第一次遍历得到链表中的有效个数
        int sum = getLength(headNode);
        if (sum >= n || 0 <= n) {
            HeroNode temp = headNode.next;
            for (int i = 0; i < sum - n; i++) {
                temp = temp.next;
            }
            return temp;
        }
        return null;
    }

    //遍历链表
    public void list() {
        //先判断链表是否为空
        if (headNode.next == null) {
            System.out.println("链表为空,不能遍历!");
            return;
        }
        //不为空,那么向后遍历,头结点不能动
        HeroNode temp = headNode.next;
        while (true) {
            if (temp == null) {
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }

    //链表的反转

    /**
     * 1.先创建一个reversalHeadNode
     * 2.将原链表的第一个元素摘下来
     * 3.将摘下来的元素放到reversalHeadNode后边
     * 4.重复第二步操作
     * 5.将摘下来的元素再次插入到reversalHeadNode后边
     * 6.以此反复
     * 7.最后将headNode指向反转链表,让reversalHeadNode指向空
     */
    public void reversalList(HeroNode headNode) {
        //如果当前链表为空或者只有一个元素,直接返回
        if (headNode.next == null || headNode.next.next == null) {
            return;
        }
        //定义一个辅助指针 帮助我们遍历原来的链表
        HeroNode cur = headNode.next;
        //指向当前一个节点的下一个节点
        HeroNode next = null;
        //定义一个新的链表头节点
        HeroNode reversalHead = new HeroNode(0, "", "");
        //遍历原始链表  每遍历一个节点,就将其取出,并放在新的链表的最前端
        while (cur != null) {
            next = cur.next;//先暂时保存当前节点的下一个节点,因为后面要使用
            cur.next = reversalHead.next;//将cur的下一个节点指向新的链表的最前端
            reversalHead.next = cur;//将cur连接到新的链表上
            cur = next;//让cur的后移
        }
        headNode.next = reversalHead.next;
    }

    /**
     * 从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】
     * 第一种方式: 会破坏原始链表的结构 不可取
     * 第二种方式: 用栈的特性来辅助完成操作 栈是一种先进后出的数据结构
     *
     * @param headNode
     */
    public void reversalPrint(HeroNode headNode) {
        //如果链表为空或者链表只有一个元素则直接返回
        if (headNode.next == null | headNode.next.next == null) {
            return;
        }
        //创建一个栈
        Stack<HeroNode> stack = new Stack<>();
        //定义一个辅助指针
        HeroNode temp = headNode.next;
        //将链表中的元素压入栈中
        while (temp != null) {
            stack.add(temp);
            temp = temp.next;
        }
        //出栈
        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }

    /**
     * 合并两个有序的单链表,合并之后的链表依然有序
     * 思路
     * 1.创建一个新的链表用于存放合并的两边元素
     * 2.每次从两个链表中取出两个元素,比较大小,然后小的加到新的链表
     * 注意 这里是以水浒传的编号进行比较的
     *
     * @param headNode0
     * @param headNode1
     */
    public SingleLinkedList mergeList(HeroNode headNode0, HeroNode headNode1) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        if (headNode0.next == null & headNode1.next == null) {
            return null;
        }
        HeroNode temp = headNode0;
        HeroNode next = null;
        while (temp!=null) {
            next = temp.next;
            singleLinkedList.addByOrder(temp);
            temp = next;

        }
        temp = headNode1;
        while (temp!=null) {
            next = temp.next;
            singleLinkedList.addByOrder(temp);
            temp = next;

        }
        return singleLinkedList;
    }
}

SingleLinkedListDemo类

package com.datestructures.linkedlist;

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        HeroNode heroNode1=new HeroNode(1, "宋江", "及时雨");
        HeroNode heroNode2=new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode heroNode3=new HeroNode(3, "吴用", "智多星");
        HeroNode heroNode4=new HeroNode(4, "林冲", "豹子头");
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        /*
        //顺序加入
        singleLinkedList.add(heroNode1);
        singleLinkedList.add(heroNode2);
        singleLinkedList.add(heroNode3);
        singleLinkedList.add(heroNode4);*/
        //自动排序加入
        singleLinkedList.addByOrder(heroNode1);
        singleLinkedList.addByOrder(heroNode4);
        singleLinkedList.addByOrder(heroNode3);
        singleLinkedList.addByOrder(heroNode2);
        //重复加入
        singleLinkedList.addByOrder(heroNode2);
        //先显示一次
        singleLinkedList.list();
        //new 一个需要修改的对象
        HeroNode newHeroNode=new HeroNode(2, "小卢", "玉麒麟~~");
        singleLinkedList.update(newHeroNode);
        System.out.println("修改后的链表为:");
        //面试题  求单链表中有效节点的个数
        System.out.println("有效节点的个数为:"+singleLinkedList.getLength(singleLinkedList.getHeadNode()));
        singleLinkedList.list();
        //从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】
        System.out.println("从尾到头打印单链表为");
        singleLinkedList.reversalPrint(singleLinkedList.getHeadNode());
        System.out.println("原始链表为");
        singleLinkedList.list();
        singleLinkedList.reversalList(singleLinkedList.getHeadNode());
        //单链表的反转【腾讯面试题,有点难度】
        System.out.println("反转后的链表为");
        singleLinkedList.list();
        //查找单链表中的倒数第k个结点 【新浪面试题】 测试
        System.out.println("查找单链表中的倒数第k个结点为"+singleLinkedList.getHeroNode(0));
        //删除操作
        singleLinkedList.delete(1);
        singleLinkedList.delete(4);
        singleLinkedList.delete(2);
        singleLinkedList.delete(3);
        System.out.println("修改后的链表为:");
        singleLinkedList.list();
        System.out.println("+++++++++++++++");
        HeroNode heroNode11=new HeroNode(1, "宋江", "及时雨");
        HeroNode heroNode22=new HeroNode(4, "卢俊义", "玉麒麟");
        HeroNode heroNode33=new HeroNode(5, "吴用", "智多星");
        HeroNode heroNode44=new HeroNode(7, "林冲", "豹子头");
        HeroNode heroNode5=new HeroNode(2, "宋江", "及时雨");
        HeroNode heroNode6=new HeroNode(6, "卢俊义", "玉麒麟");
        HeroNode heroNode7=new HeroNode(7, "吴用", "智多星");
        HeroNode heroNode8=new HeroNode(8, "林冲", "豹子头");
        SingleLinkedList singleLinkedList1 = new SingleLinkedList();
        singleLinkedList.addByOrder(heroNode11);
        singleLinkedList.addByOrder(heroNode44);
        singleLinkedList.addByOrder(heroNode33);
        singleLinkedList.addByOrder(heroNode22);
        singleLinkedList1.addByOrder(heroNode5);
        singleLinkedList1.addByOrder(heroNode6);
        singleLinkedList1.addByOrder(heroNode7);
        singleLinkedList1.addByOrder(heroNode8);
        System.out.println("合并前的链表分别为");
        System.out.println("链表一");
        singleLinkedList.list();
        System.out.println("链表二");
        singleLinkedList1.list();
        SingleLinkedList singleLinkedList2 = new SingleLinkedList().mergeList(heroNode11,heroNode5);
        System.out.println("合并后的链表为~~");
        singleLinkedList2.list();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值