【JAVA】链表

5 篇文章 0 订阅
1 篇文章 0 订阅

线性表

1、顺序表——基于数组实现的
数组的缺点:
1)数组需要开发者自己维护下标
2)数组开辟时必须指定数组的长度,如果存放的元素过多,需要开发者自己扩容数组
3)当在数组的某些位置增加和删除元素时,还要编写代码处理元素的移动

2、单链表
在这里插入图片描述
1)链表中每个元素包含一个称为节点的结构
2)节点 = 数据域 + 引用域
3)链表是一种物理存储单元上非连续、非顺序的存储结构
4)链表在插入元素可以达到O(1)的复杂度,相比顺序表更快
5)查找或访问特定编号的节点则需要O(n)的时间

3.双向链表
在单链表的结点中增加一个指向其前驱的pre指针。

在这里插入图片描述
例题:
1.逆序输出链表

思想:
(1) 定义一个中间容器数组存放数据,逆序输出
(2) 定义一个栈对象,压栈,出栈,实现逆序
(3) 递归实现

public static <E> void reversePrintList(SingleLinkedList<E>.Node<E> head){
        //递归终止条件
        if(head == null){
            return;//处理办法
        }
        //提取重复逻辑,缩小问题规模
        reversePrintList(head.next);
        System.out.println(head.element + " ");
    }

2.逆置链表
思想:定义前驱指针

public static <E> SingleLinkedList<E>.Node<E> reverseList(SingleLinkedList<E>.Node<E> head){
        SingleLinkedList<E>.Node<E> current = head; //当前节点
        SingleLinkedList<E>.Node<E> prev = null;//当前节点的前一个
        SingleLinkedList<E>.Node<E> newHead = null;//逆置后链表的头节点

        while(current != null){
            //当前节点的下一个
            SingleLinkedList<E>.Node<E> next = current.next;
            if(next == null){
                newHead = current;
            }
            current.next = prev;
            prev = current;
            current = next;
        }
        return newHead;
    }

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

public <E extends Comparable>void hebin(Node<E> head1,Node<E> head2){
        //定义新链表的头结点
        Node curHead; 
        //初始化新链表的头结点
        if(head1.element.compareTo(head2.element) == -1){
            curHead = head1;
            head1 = head1.next;
        }else{
            curHead = head2;
            head2 = head2.next;
        }
        //定义临时引用变量,添加新链表
        Node tmp = curHead;
        while (head1 != null && head2 != null){
            if(head1.element.compareTo(head2.element) == -1){
                tmp.next = head1.next;
                head1 = head1.next;
            }else {
                tmp.next = head2.next;
                head2 = head2.next;
            }
            tmp = tmp.next;
        }
        if(head1 == null){
            tmp.next = head2;
        }
        if(head2 == null){
            tmp.next = head1;
        }
    }

4.查找单链表中倒数第K个元素
思想:
定义两个引用变量front,behind,让front先走K个,然后behind与front同步走,当front走到尾时,behind刚好为倒数第K个元素,front与behind之间相差K-1 个元素

public static <E> SingleLinkedList1<E>.Node<E> lastK(
            SingleLinkedList1<E>.Node<E> head, int k){
        if(head == null || k<0)  return null;
        //控制时间复杂度为O(n),只需要遍历链表一次实现
        SingleLinkedList1<E>.Node<E> front = head;
        SingleLinkedList1<E>.Node<E> behind = head;

        for(int i=0; i<k-1; i++){
            if(front.next != null){
                front = front.next;
            }else{
                return null;
            }
        }

        while(front.next != null){
            front = front.next;
            behind = behind.next;
        }
        return behind;
    }

5.不允许遍历链表, 在 pos之前插入
思想:
由于单向链表的特性引用变量只能向后走,所以只 能将数据插入pos后,然后将pos节点与新节点的数据域交换

public static <E> boolean insertPosBefore(
            SingleLinkedList1<E> list,SingleLinkedList1<E>.Node<E> pos, E data){
        if(list.getHead() == null || pos == null) return false;

        //创建新节点
        SingleLinkedList1<E>.Node<E> newNode = list.createNode(pos.element);

        //插入新节点至pos之后
        newNode.next = pos.next;
        pos.next = newNode;

        //改变pos位置的element为data
        pos.element = data;
        return true;
    }

6.两个链表相交,输出相交节点

  public static <E> SingleLinkedList<E>.Node<E> commonNode(
            SingleLinkedList<E> list1, SingleLinkedList<E> list2){
        if(list1.getHead() == null || list2.getHead() == null) return null;

        //计算两个链表的差值
        int length1 = list1.getLength();
        int length2 = list2.getLength();
        int lengthDif = Math.abs(length1-length2);

        SingleLinkedList<E>.Node<E> longHead = list1.getHead();
        SingleLinkedList<E>.Node<E> shortHead = list2.getHead();

        if(length1 < length2){
            longHead = list2.getHead();;
            shortHead = list1.getHead();
        }

        //长链表先走
        for(int i=0; i<lengthDif; i++){
            longHead = longHead.next;
        }

        //两个指针同时走
        while(longHead != shortHead){
            longHead = longHead.next;
            shortHead = shortHead.next;
        }
        return longHead;
 }

测试:(构造两个相交的链表)

 public static void main(String[] args) {
        //构造两个相交的链表
        SingleLinkedList1<Integer> list1 = new SingleLinkedList1<>();
        list1.add(1);list1.add(3);list1.add(5); list1.add(7); list1.add(9);

        SingleLinkedList1<Integer> list2 = new SingleLinkedList1<>();
        list2.add(0);list2.add(2);list2.add(4);list2.add(6);

        list1.show();
        list2.show();

        SingleLinkedList1<Integer>.Node<Integer> head1= list1.getHead();
        while(head1.element != 7){
            head1 = head1.next;
        }

        SingleLinkedList1<Integer>.Node<Integer> head2 = list2.getHead();
        while(head2.next != null){
            head2 = head2.next;
        }
        head2.next = head1;
        list1.show();
        list2.show();
 }

7.单链表是否有环,环的入口节点是哪个
思想:
(1)定义快慢指针找相遇节点,然后计算环的长度,类比找倒数第K个元素找到环的入口节点。

public static <E> SingleLinkedList1<E>.Node<E> ringNode(
            SingleLinkedList1<E>.Node<E> head){
        //判断是否存在环,如果存在,则已知环中任意节点
        SingleLinkedList1<E>.Node<E> meeetingNode = isRing(head);
        //说明环不存在
        if(meeetingNode == null){
            return null;
        }
        SingleLinkedList1<E>.Node<E> front = meeetingNode.next;
        int length = 1;
        //计算环中节点的个数 length
        while (front.next != front ) {
            length++;
            front = front.next;
        }
        //类比求倒数第k个节点的算法
        front = head;
        SingleLinkedList1<E>.Node<E> behind = head;

        for(int i=0; i<length; i++){
            front = front.next;
        }
        while(front.next != behind){
            front = front.next;
            behind = behind.next;
        }
        return behind;
    }
public static <E> SingleLinkedList1<E>.Node<E> isRing(SingleLinkedList1<E>.Node<E> head){
        if(head == null) return null;

        SingleLinkedList1<E>.Node<E> slow = head.next;
        //链表中只有一个头节点
        if(slow == null) return null;
        SingleLinkedList1<E>.Node<E> fast = slow.next;
        while(fast != null && slow != null && fast.next != null){
            if(fast == slow){
                return slow;
            }
            slow = slow.next;
            fast = fast.next.next;
            fast = fast.next;
            if(fast.next != null){
                fast = fast.next;
            }
        }
        return slow;
    }

(2)
在这里插入图片描述

public static <E> SingleLinkedList1<E>.Node<E> isRing(SingleLinkedList1<E>.Node<E> head){
        if(head == null) return null;
        //定义快慢指针一起走找相遇点
        SingleLinkedList1<E>.Node<E> slow = head.next;
        //链表中只有一个头节点
        if(slow == null) return null;
        SingleLinkedList1<E>.Node<E> fast = slow.next;
        while(fast != null && slow != null && fast.next != null){
            if(fast == slow){
                return slow;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        //前后引用变量一起走,再次相遇即为环入口节点
        fast = head;
        while (fast != slow){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值