数据结构与算法(4):单链表面试题:(1.查找有效节点2.查找单链表中的倒数第K个节点3.单链表的反转4.从尾到头打印单链表(要求方式:反向遍历,方式二:Stack栈)5.合并两个单链表,使之有序)

单链表的常见面试题有如下:

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

**思路:**查找有效节点的个数,首先想到的就是遍历这个链表
代码演示:

//head 为头结点
    public static int getLength(StudentNode head){
        if (head.next == null){
            //说明这是一个空链表
            return 0;
        }
        int length =0;
        //定义了一个辅助接点,让辅助节点指向了头下一个节点
        StudentNode node = head.next;
        while(node !=null){
            length++;
            node = node.next;
        }
        return length;
    }

}

主函数代码直接调用方法:

   //表示头结点不存储任何数据
    private StudentNode head = new StudentNode(0,"","");

    public StudentNode getHead() {
        return head;
    }

 System.out.println("有效的节点个数为:"+getLength(linkedList.getHead()));

代码运行如下:
在这里插入图片描述

2.查找单链表中的倒数第K个节点(新浪面试题)

思路:
1.编写一个方法,接受head节点,同时接受一个index
2.index:表示是倒数的第index个节点
3.先把链表从头到尾遍历,得到了链表的总的长度,然后在用总的长度减去index就实现了倒数的查找.(length-index = k)
4.如果找到了这个k,就返回该节点,否则就返回空
代码实现:

 public static StudentNode findLastNode(StudentNode head,int index){
        if (head.next ==null){
            return null;
        }
        //第一次遍历得到链表的长度
        int size = getLength(head);
        //第二次遍历
        //遍历的次数为:size-index
        //先做一个index的校验
        if (index <= 0||index > size){
            return null;
        }
        //定义一个辅助变量,for循环定位到倒数的index个
        StudentNode temp = head.next;
        for (int i = 0; i <size-index ; i++) {
            //size是总长度,index是倒数的节点,这里的for循环是为了找到我们要移动这个辅助变量多少次,
            temp = temp.next;
        }
        return temp;
    }

主函数代码:

 //测试是否得到第k个节点
        StudentNode res = findLastNode(linkedList.getHead(),1);
        System.out.println("res="+res);

代码运行如下图:

在这里插入图片描述

3.单链表的反转(腾讯面试题)

思路:
1.先定义一个节点,reverseHead = new StudentNode();

2.从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放到新的链表reverseHead的最前端,

3.遍历到的第二个节点会将其放在前一个节点的前端,循环如此

4.最后再将原先的head节点指向这个新节点的头部

代码实现:

   public static  void  reverseList(StudentNode head){
        //当前列表为空,或者说只有一个节点
        if (head.next == null||head.next.next == null){
            return;
        }
        //定义一个辅助的变量,帮助我们遍历原来的链表
        StudentNode current = head.next;
        StudentNode next = null;//指向当前节点temp的下一个节点
        StudentNode reverseHead = new StudentNode(0,"","");//新的节点的头结点
        //遍历原来的链表,每遍历一个节点就将其取出,并放在新的链表reverseHead的最前端
        while(current != null){
            next = current.next;//先暂时保存当前节点的下一个节点,后面需要
            current.next = reverseHead.next;//将current的下一个节点指向新的链表的头结点的下一个节点
            reverseHead.next = current;
            current= next;//让current后移,由于将current节点拿走了,那么我们需要保留current的下一个节点,防止链表断裂

        }
        //将head.next指向reverseHead.next,实现单链表的反转
        head.next = reverseHead.next;
    }

主函数代码:

 System.out.println("测试一个原链表的情况===========");
        linkedList.list();
        System.out.println("测试一个反转单链表的情况======");
        reverseList(linkedList.getHead());
        linkedList.list();

这里面有一个需要思考的代码,就是这一段:

//定义一个辅助的变量,帮助我们遍历原来的链表
        StudentNode current = head.next;
        StudentNode next = null;//指向当前节点temp的下一个节点
        StudentNode reverseHead = new StudentNode(0,"","");//新的节点的头结点
        //遍历原来的链表,每遍历一个节点就将其取出,并放在新的链表reverseHead的最前端
        while(current != null){
            next = current.next;//先暂时保存当前节点的下一个节点,后面需要
            current.next = reverseHead.next;//将current的下一个节点指向新的链表的头结点的下一个节点
            reverseHead.next = current;
            current= next;//让current后移,由于将current节点拿走了,那么我们需要保留current的下一个节点,防止链表断裂

        }
        //将head.next指向reverseHead.next,实现单链表的反转
        head.next = reverseHead.next;
    }

这里我讲讲我的理解:

1.首先,我们定义了一个原先表的辅助变量为current
StudentNode current = head.next;

2. 接着我们定义了一个原先链表的next节点,这个节点将它当成这个current的下一个节点,这里只做了定义没做赋值,
StudentNode next = null;//指向当前节点current的下一个节点

3.我们定义了一个新的链表头,目的就是将原先链表中后面节点的值取出来,然后在放到新的链表中去
StudentNode reverseHead = new StudentNode(0,"","");//新的节点的头结点

4.然后我们执行循环,遍历出原先链表中的每一个节点
while(current != null)//这里简单的做了一个判断原先链表不为空

5.最重要的部分来了,我们将当前节点,也就是头结点的下一个节点(第一个有值的节点)赋值给next,(具体next看2)
next = current.next;//先暂时保存当前节点的下一个节点,后面需要

6.当current的下一个节点指向reverseHead的下一个节点,也就是将原先遍历到的每一个节点都赋给新的头结点的next
current.next = reverseHead.next;//将current的下一个节点指向新的链表的头结点的下一个节点

7.我们将current,赋给了新的链表的头结点的下一个节点
reverseHead.next = current;

8.因为我们前面已经存好了当前节点的下一个节点,所以说:这一步我们再将第一个有值节点的下一个节点赋给current(现在的当前节点),这一步就是为了将currnet后移
current= next;//让current后移,由于将current节点拿走了,那么我们需要保留current的下一个节点,防止链表断裂

9.最后一步:我们将新的链表反转了,但是头结点并不是原先的哪一个,所以我们这一步就是将反转后的新头结点,赋值给原来的头结点.这样最终实现了原先节点的反转
//将head.next指向reverseHead.next,实现单链表的反转
head.next = reverseHead.next;

4.从尾到头打印单链表(百度:要求方式:反向遍历,方式二:Stack栈)

思路分析:
1.从尾到头打印链表,就是逆序打印单链表
方式一:先将单链表反转过来,再进行遍历打印(问题:只是打印,但是破坏了单链表的结构,不建议)

方式二:可以利用栈这个数据结构,将各个节点压入栈中,再利用栈的先进后出的特点,实现了逆序打印的效果(栈:)

代码演示:

//使用方式二,进行逆序的打印这个链表.可以利用栈的这个结构,将各个节点压入到栈中然后再利用栈的先进后出的特点
    public static void reversePrint(StudentNode head){
        if (head.next ==null){
            System.out.println("该链表为空,无法打印这个链表");
            return;
        }
        Stack<StudentNode> stack = new Stack<>();
       StudentNode current =  head.next;
       //将链表的所有节点压入栈中
       while (current != null){
           stack.push(current);
           //每压入一次就往后面移动一次
           current  = current.next;
       }
       //将栈中的节点打印出来
        while(stack.size()>0){
            System.out.println(stack.pop());
        }
    }

主函数代码:

System.out.println("测试逆序打印单链表");
        reversePrint(linkedList.getHead());

代码运行结果展示:
在这里插入图片描述

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

思路分析:合并两个有序的单链表,可以再利用一个新的链表来进行操作
1.我们不知道两个有序的单链表那个key值更小,所以我们需要进行判断,从小到大排列,
2.比较两个链表中的最小值,并放到新的链表中依次执行

代码展示:
创建两个链表代码:

 //先创建几个节点
        StudentNode student1 =  new StudentNode(1,"qiuzhikang","91");
        StudentNode student3 =new StudentNode(3,"chenxiaocong","81");
        StudentNode student5 =new StudentNode(5,"diyifei","71");
        StudentNode student7 =new StudentNode(7,"xujinbo","61");

        //第二个链表
        StudentNode student2 =  new StudentNode(2,"tangzaizai","11");
        StudentNode student4 =new StudentNode(4,"wulinjie","21");
        StudentNode student6 =new StudentNode(6,"dengshiyuan","31");
        StudentNode student8 =new StudentNode(8,"honghuanming","41");
        //创建一个链表
        SingleLinkedList linkedList = new SingleLinkedList();
        SingleLinkedList linkedList2 = new SingleLinkedList();//为了合并两个有序的链表,我再新建一个有序的链表
//        linkedList.add(student1);
//        linkedList.add(student2);
//        linkedList.add(student3);
//        linkedList.add(student4);
        //加入按照编号的顺序
        linkedList.addByRank(student1);
        linkedList.addByRank(student3);
        linkedList.addByRank(student5);
        linkedList.addByRank(student7);

        //第二个有序链表
        linkedList2.addByRank(student2);
        linkedList2.addByRank(student4);
        linkedList2.addByRank(student6);
        linkedList2.addByRank(student8);

    //5.合并两个有序的单链表,合并之后的链表依然有序
    public static  void formTwoLinkedList(StudentNode head,StudentNode head2) throws Exception {
        StudentNode temp1 = head.next;
        StudentNode temp2 = head2.next;
        //新的链表节点
        StudentNode newNode = new StudentNode(0,"","");
        //接下来做判断
        //1.第一个链表没有节点,则只显示第二个链表的节点
        if (head.next ==null){
            newNode.next= head2.next;
        }
        if (head2.next ==null){
            newNode.next= head.next;
        }
        if (head == null&&head2 == null){
            throw new Exception("两张链表均为空");
        }
        StudentNode temp3 = newNode;//新节点的临时变量
        //第一个链表不为空,或者第二个链表不为空
        //1.链表一不为空,链表二为空.2链表一位空,链表二不为空.3.链表一不为空,链表二不为空
        while(temp1!=null || temp2 != null) {
            if (temp1 != null && temp2 == null) {
                temp3.next = temp1;//让temp3指向temp1的节点
                temp1 = temp1.next;
            } else if (temp1 == null && temp2 != null) {
                temp3.next = temp2;//让temp3指向temp2的节点
                temp2 = temp2.next;
            } else {
                //都不为空的情况下,合并两个有序的链表
                if (temp1.rank >= temp2.rank) {
                    temp3.next = temp2;
                    temp2 = temp2.next;
                } else {
                    temp3.next = temp1;
                    temp1 = temp1.next;
                }
            }
            temp3 = temp3.next;
        }
            SingleLinkedList list = new SingleLinkedList();
            list.show(newNode);
        }

主函数代码:

 formTwoLinkedList(linkedList.getHead(),linkedList2.getHead());

代码执行结果如下图:

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值