2020-12-11

本文详细介绍了单链表的基本概念,并通过Java代码展示了如何创建和操作单链表,包括节点插入、链表逆序打印、查找倒数第k个节点、合并有序链表以及判断链表是否有环并找到环的入口点等常见问题的解决方案。通过这些实例,深入理解了单链表的数据结构和操作技巧。
摘要由CSDN通过智能技术生成
                              单链表

一、单链表的概念

链表是最基本的数据结构,其存储的你原理图如下图
在这里插入图片描述
head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,其中的next指向null。

二、用java实现单链表

2.1、编写一个Node类来充当结点的模型。

其中有两个属性,1.存放数据的data。2.存放下一结点的引用。
在这里插入图片描述

2.2、单链表的简单操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、单链表的常见题型

3.1、逆序打印单链表

在这里插入图片描述

3.2、单链表逆置

在这里插入图片描述

在这里插入图片描述

3.3、不允许遍历链表,在pos节点之前插入一个新节点

public boolean addPos(T value, int pos){
    //new一个新节点 pos-1的节点 在pos-1位置之后链上当前的newNod
    Node<T> newNode = new Node(value);
    if(pos < 0 || pos >= getLength()){
        return false;
    }
    if(pos == 0){
        newNode.next = head;
        head = newNode;
        return true;
    }
    //pos-1位置的节点
    Node<T> tmp = head;
    for(int i=0; i<=pos -1; i++){
        tmp = tmp.next;
    }
    //i>pos- 1 tmp刚好指向pos-1位置的节点
    newNode.next = tmp.next;
    tmp.next = newNode;
    return true;
}

3.4、查找单链表中倒数第k个节点

在这里插入图片描述

3.5、合并两个有序的链表,保证合并之后的链表依然有序在这里插入图片描述


```java

```java
class SingleListTestDemo2 {
    //合并两个有序的链表,保证合并之后的链表依然有序
    //1 3 5 7 9
    //2 4 6
    //-》1 2 3 4 5 6 7 9
    //泛型类型 涉及到比较大小  类型擦除到Object类型,Object类型中没有提供比较大小的方法
    //但是Comparable接口提供了,期望类型擦除到Comparable接口
    //泛型类型可以定义上界  通过extends关键字  使其擦除到上界所对应的类型
    public static <T extends Comparable<T>> MySingleList<T>.Node<T> mergeOrderList(MySingleList<T>.Node<T> head1, MySingleList<T>.Node<T> head2) {
        if(head1 == null && head2 == null){
            return null;
        }else if(head1 == null){
            return head2;
        }else if(head2 == null){
            return head1;
        }
        //其中就可以使用compareTo方法比较大小
        //要求所传的泛型类型也必须要实现
        MySingleList<T>.Node<T> newHead = null;
        //确定合并后链表的头节点,利用CompareTo方法比较head1与head2头的大小
        if (head1.getData().compareTo(head2.getData()) >= 0) {
            //head1头大于head2头新链表头指定为head2
            newHead = head2;
            //head2头往下走
            head2 = head2.getNext();
        } else {
            newHead = head1;
            head1 = head1.getNext();
        }
        //遍历list1和list2,将对应位置更小的节点链到新链表之后
        //一旦list1和list2其中一个遍历完成,则直接链入另外一个未遍历完的链表
        MySingleList<T>.Node<T> tmp = newHead;
        while (head1 != null && head2 != null) {
            if (head1.getData().compareTo(head2.getData()) >= 0) {
                //head2指向新链表尾部
                tmp.setNext(head2);
                //head2为更小的位置点,head2需要往后走
                head2 = head2.getNext();
                //tmp永远指向新链表尾巴
                tmp = tmp.getNext();
            } else {
                tmp.setNext(head1);
                //head1为更小的位置点,head1需要往后走
                head1 = head1.getNext();
                //tmp永远指向新链表尾巴
                tmp = tmp.getNext();
            }
        }
        if (head1 == null) {
            tmp.setNext(head2);
        }
        if (head2 == null) {
            tmp.setNext(head1);
        }
        return newHead;
    }
    public static void main(String[] args) {
        MySingleList<Integer> list1 = new MySingleList<>();
        MySingleList<Integer> list2 = new MySingleList<>();

        list1.addTail(1);
        list1.addTail(5);
        list1.addTail(6);
        list1.addTail(8);
        list1.addTail(10);
        list1.addTail(11);
        list1.addTail(15);

        list2.addTail(2);
        list2.addTail(4);
        list2.addTail(6);
        list2.addTail(8);

        System.out.println(list1.toString());
        System.out.println(list2.toString());
        MySingleList<Integer>.Node<Integer> newHead = mergeOrderList(list1.getHead(), list2.getHead());
        //打印新的链表
        if(newHead != null){
            //定义一个tmp保存newHead链表
            MySingleList<Integer>.Node<Integer> tmp = newHead;
            while(tmp != null){
                System.out.print(tmp.getData()+" ");
                tmp = tmp.getNext();
            }
            System.out.println();
        }
    }
}

3.6、两个链表相交,求出相交节点

 1、两个链表相交,求出相交节点
public class TestDemo12_10 {
    public static <T>  MySingleList<T>.Node<T> commonNode1(MySingleList<T>.Node<T> head1, MySingleList<T>.Node<T> head2){
        if(head1 == null || head2 == null){
            return null;
        }
        //将两个链表中的元素进行入栈操作
        Stack<MySingleList<T>.Node<T>> stack1 = new Stack<>();
        Stack<MySingleList<T>.Node<T>> stack2 = new Stack<>();

        //list1
        MySingleList<T>.Node<T> tmp = head1;
        while(tmp != null){
            stack1.push(tmp);
            tmp = tmp.getNext();
        }
        //list2
        tmp = head2;
        while(tmp != null){
            stack2.push(tmp);
            tmp = tmp.getNext();
        }

        MySingleList<T>.Node<T> commonNode = null;
        //判断栈顶元素是否相等
        while(stack1.peek() == stack2.peek()){
            //给定一个参数保存栈顶元素
            commonNode = stack1.peek();
            stack1.pop();
            stack2.pop();
        }
        //返回相交节点
        return commonNode;
    }
    public static void main(String[] args) {
        //构造两个相交的链表
        MySingleList<Integer> list1 = new MySingleList<>();
        MySingleList<Integer> list2 = new MySingleList<>();
        list1.addTail(1);
        list1.addTail(10);
        list1.addTail(5);
        list1.addTail(7);
        list1.addTail(6);
        list1.addTail(19);
        list1.addTail(20);

        MySingleList<Integer>.Node<Integer> tmp = list1.getHead();
        MySingleList<Integer>.Node<Integer> commonNode = null;
        //先找到你要相交的节点,比如6号元素所在节点,那你就找datd域为6的这个节点
        while(tmp != null){
            if(tmp.getData() == 6){
                commonNode = tmp;
                break;
            }
            tmp = tmp.getNext();
        }
        list2.addTail(3);
        list2.addTail(5);
        tmp = list2.getHead();
        //找到list1的尾节点
        while(tmp.getNext() != null){
            tmp = tmp.getNext();
        }
        //在当前链表尾节点之后拼接上这个6号节点
        tmp.setNext(commonNode);
        System.out.println(list1.toString());
        System.out.println(list2.toString());
        MySingleList<Integer>.Node<Integer> result1 = commonNode1(list1.getHead(), list2.getHead());
        if(result1 != null ){
            System.out.println(result1.getData());
        }
    }
}

3.7、判断链表是否带环,并求环的入口点

/**
 * 判断一个链表是否有环,求出环的入口节点
 */
public class TestDemo12_10 {
    //判断是否有环
    public static <T>  MySingleList<T>.Node<T> isRing(MySingleList<T>.Node<T> head){
        if(head == null){
            return null;
        }
        //定义快慢指针
        MySingleList<T>.Node<T> slow = head.getNext();
        //链表中只有一个节点
        if(slow == null){
            return null;
        }
        MySingleList<T>.Node<T> fast = slow.getNext();

        while(fast != null && fast.getNext() != null && slow != null){
            //快指针和慢指针指向相同的节点(相遇)
            if(slow == fast){
                return slow;
            }
            //快指针走两步,慢指针走一步
            slow = slow.getNext();
            fast = fast.getNext().getNext();
        }
        return null;
    }
    //求环的入口节点
    public static <T> MySingleList<T>.Node<T> entranceNode(MySingleList<T>.Node<T> head){
        //meetingNode存在于环中
        MySingleList<T>.Node<T> meetingNode = isRing(head);
        //计算环中节点的个数
        int length = 1;
        MySingleList<T>.Node<T> tmp = meetingNode;
        //走一圈判断环的长度
        while(tmp.getNext() != meetingNode){
            tmp = tmp.getNext();
            length++;
        }
        //类比求倒数第k个节点的算法,相当于数学里面的追击问题
        //front先走length  front, behind开始同时走  front != behind
        MySingleList<T>.Node<T> front = head;
        MySingleList<T>.Node<T> behind = head;
        for(int i=0; i<length ;i++){
            front = front.getNext();
        }
        //front和behind同时走
        while(front != behind){
            front = front.getNext();
            behind = behind.getNext();
        }
        return front;
    }
    public static void main(String[] args) {
        //构造一个带环的单链表
        MySingleList<Integer> list = new MySingleList<>();
        list.addTail(10);
        list.addTail(1);
        list.addTail(0);
        list.addTail(9);
        list.addTail(4);
        list.addTail(15);
        list.addTail(20);
        list.addTail(6);
        list.addTail(8);
        MySingleList<Integer>.Node<Integer> tmp1 = list.getHead();
        //找到链表尾部
        while(tmp1.getNext() != null){
            tmp1 = tmp1.getNext();
        }
        MySingleList<Integer>.Node<Integer> tmp2 = list.getHead();
        //指定环节点
        while(tmp2.getData() != 4){
            tmp2 = tmp2.getNext();
        }
        //将指定的环节点插入到链表尾部形成环
        tmp1.setNext(tmp2);
        MySingleList<Integer>.Node<Integer> entranceNode =  entranceNode(list.getHead());
        if(entranceNode != null){
            System.out.println("环的入口节点:"+entranceNode.getData());
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值