数据结构之单链表(百度面试题,逆序打印单链表)

package com.ws.链表.单向链表.单链表面试题;

//1)求单链表中有效节点个数

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

//3)单链表的反转【腾讯面试题】

//4)从尾到头打印单链表【百度 1.反向遍历  2.Stack栈】




import java.util.Stack;

//注意:在链表遍历时,要从头的下一个开始遍历:Node listNode=head.next;
//                判断是否到链表尾时是判断当前对象当前节点是否为空,以为添加了节点,要是当前节点下一个的话,就是另一个节点了
//                ,而循环时已经指向下一个节点了,这是和添加节点遍历得到链表尾时是不同的
public class LinkedList {
    public static void main(String[] args) {
        //进行测试
        //创建节点
        Node node1=new Node(1,"王","小王");
        Node node2=new Node(2,"李","小李");
        Node node3=new Node(3,"周","小周");
        Node node4=new Node(4,"马","小马");

        //创建链表
        NodeList nodeList=new NodeList();
        //加入
        nodeList.addNode(node1);
        nodeList.addNode(node4);
        nodeList.addNode(node2);
        nodeList.addNode(node3);

        //修改节点
        nodeList.list();
        Node node=new Node(2,"李","修改李");
        nodeList.update(node);
        System.out.println("修改后的链表");
        //显示
        nodeList.list();
        //删除2节点
        System.out.println("删除2节点");
        nodeList.delect(2);
        System.out.println("删除后");
        nodeList.list();


        //测试单链表的有效节点个数
        System.out.println("有效地节点个数为:"+getLength(nodeList.getHead()));
        //测试是否得到倒数第k个节点
        System.out.println("倒数第2个节点是"+getfindk(nodeList.getHead(), 2));

        //单链表的反转
        System.out.println("单链表的反转");
        reverList(nodeList.getHead());
        nodeList.list();

        //逆序打印单链表
        System.out.println("逆序打印单链表,没改变链表结构");
        reversPrint(nodeList.getHead());

        

    }

    //面试题:
    //1)获取单链表的节点个数(如果带头结点的链表,需要不统计头结点)
    //头节点的下一个遍历自加
    public static int getLength(Node head){
        //判断是否为空
        if (head.next==null){
            return 0;
        }
        int length=0;
        //头的后一个节点
        Node cur=head.next;
        while (cur!=null){
            length++;
            cur=cur.next;
        }
        return length;
    }

    //2)查找单链表中的倒数第k个节点【新浪面试题】
    //1.传入头结点和倒数第k个
    //2.链表先从头到尾遍历,得到总长度
    //3.得到长度后从头遍历(长度-k)个,得到
    //4,。找到就返回,否则返回null
    public static Node getfindk(Node head,int size){
        //判断链表是否为空,返回null
        if (head.next==null){
            return null;//没有找到
        }
        //遍历得到链表长度
        int length=getLength(head);
        //遍历(length-size)位置,就是需要节点
        //判断size是否合理
        if (size<=0||size>length){
            return null;
        }
        //头结点的分身遍历
        Node listNode=head.next;
        //循环定位到倒数的size个位置
        for (int i=0;i<length-size;i++){
            listNode=listNode.next;
        }
        return listNode;
    }

    //3)单链表的反转【腾讯面试题】
    //遍历摘出,放到新链表的前面
    //
    public static void reverList(Node head){
        //当前链表为空就直接返回
        if (head.next==null||head.next.next==null){
            //没有节点或一个节点
            return;
        }
        //辅助变量,用于遍历,由于头节点不能动
        Node listNode=head.next;//就是第一个数据节点
        Node next=null;//用于固定链表,就是指向链表下一个节点,因为要移动当前节点,如果移动的话就会失去当前链表的连接,从而失去连接
        Node headxin=new Node(0,"","");//新链表的头节点
        //遍历原来的链表,并不断把节点取出,插到新链表的最前端
        while (listNode!=null){//当前位置不为空
            next=listNode.next;//保存当前节点的下一个节点
            listNode.next=headxin.next;//将新链表的下一个给当前节点的下一个
            headxin.next=listNode;//将当前节点给新链表的头
            listNode=next;//当前节点后移
        }
        head.next=headxin.next;

    }

    //4)从尾到头打印单链表【百度 1.反向遍历  2.Stack栈】使用栈
    public static void reversPrint(Node head){
        if (head.next==null){
            return;//空链表,不能打印
        }
        //创建一个栈
        Stack<Node> stack=new Stack<>();
        Node listNode=head.next;//头结点的副本,遍历
        //将链表节点压入栈
        while (listNode!=null){
            stack.push(listNode);//压入栈
            listNode=listNode.next;//后移,循环
        }
        //栈中节点打印
        while (stack.size()>0){//栈中还有元素就循环
            System.out.println(stack.pop());//出栈
        }
    }

    


}


//定义Node,每个Node对象就是一个节点
class Node{
    public int paixu;
    public String name;
    public String nickname;
    public Node next;//指向下一个节点

    //构造器
    public Node(int paixu,String name,String nickname){
        this.paixu=paixu;
        this.name=name;
        this.nickname=nickname;
    }

    public int getPaixu(){
        return paixu;
    }

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

}

//创建NodeList管理Node  就是管理节点,构建链表
//单项链表
class NodeList{
    //初始化一个头节点,头结点不动
    private Node head=new Node(0,"","");

    public Node getHead(){//返回头结点
        return head;
    }

    //添加节点到单向链表
    //当不考虑编号顺序时
    //1.找到当前链表的最后节点
    //2.将最后节点的next指向新的节点
    public void add(Node node){
        //因为头结点不能动,所以要一个副本
        Node listNode=head;
        //遍历链表找到最后
        while (true){
            //当headtu.next=null找到链表最后
            if (listNode.next==null){
                break;
            }
            //如果没有找到最后,就将节点后移
            listNode=listNode.next;
        }
        //退出while循环时,listNode就指向了链表最后
        listNode.next=node;//将最后节点添加新链表
    }

    //插入到指定位置
    public void addNode(Node node){
        //因为头节点不能动,所以要有一个副本找节点
        //找的listNode是在添加位置的前一个节点,否则插不进去,因为要node.next=list.next
        Node listNode=head;
        boolean flag=false;//标志,添加的编号是否已经存在,默认false   就是新节点编号是否已经存在
        while (true){
            if (listNode.next==null){
                //说明listNode已经在链表最后
                break;
            }
            //由小到大排序,后一个比前一个大就插入后一个的前面
            //因为要插入到添加位置的前面
            if (listNode.next.paixu>node.paixu){//位置找到
                break;
            }else if (listNode.next.paixu==node.paixu){
                //说明希望添加的序号已经存在
                flag=true;//编号已经存在
                break;
            }
            listNode=listNode.next;//后移,遍历链表
        }
        //判断编号是否已经存在
        if (flag){//找到
            System.out.printf("添加编号【%d】已经存在,不能加入\n",listNode.paixu);
        }else {
            //插入到链表中
            node.next=listNode.next;
            listNode.next=node;
        }

    }

    //修改节点信息
    //根据序号修改,序号不能更改
    public void update(Node node){
        //判断链表是否为空
        if (head.next==null){
            System.out.println("链表为空");
            return;
        }
        //根据序号找到需要修改的节点
        //一个头节点的副本
        Node listNode=head;
        boolean flag=false;//表示是否找到该节点
        while (true){
            if (listNode==null){
                //到链表的尾,啥也没找到
                break;
            }
            if (listNode.paixu==node.paixu){
                //已经找到节点
                flag=true;
                break;
            }
            listNode=listNode.next;
        }
        //判断是否找到要修改的节点
        if (flag){//找到
            listNode.name=node.name;
            listNode.nickname=node.nickname;
        }else {
            //没有找到节点
            System.out.printf("没有找到编号【%d】的节点,不能修改\n",node.next.paixu);
        }
    }

    //删除节点
    //找到要删除的节点的前一个节点
    //因为要listNode.next=listNode.next.next
    //被删除的节点将被垃圾回收机制删除
    public void delect(int paixu){
        //头不能动,要有一个副本
        Node listNode=head;
        boolean flag=false;//是否找到待删除的前一个节点
        while (true){
            if (listNode.next==null){
                //已经到链表尾,啥也没找打
                break;
            }
            if (listNode.next.paixu==paixu){
                //找到待删除节点前一个
                flag=true;
                break;
            }
            listNode=listNode.next;
        }
        //判断是否找到待删除节点
        if (flag){
            //可以删除
            listNode.next=listNode.next.next;
        }else {
            System.out.printf("要删除的【%d】节点不存在\n",paixu);
        }
    }

    //遍历单向链表
    public void list(){
        //判断链表是否为空
        if (head.next==null){
            System.out.println("链表为空");
            return;
        }
        //因为头节点不能动,所以要一个副本来遍历
        Node listNode=head.next;
        while (true){
            //判断是否到链表最后
            if (listNode==null){
                break;
            }
            //链表不为空,输出节点信息
            System.out.println(listNode);
            //将节点后移listNode
            listNode=listNode.next;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值