排序和链表面试题

一 复杂度

1 时间复杂度

最坏情况

2空间复杂度

在这里插入图片描述
一个原始数据类型的数组一般需要24字节的头信息(16个自己的对象开销,4字节用于保存长度以及4个填充字节)再加上保存值所需的内存

二 排序

简单排序

(1) 冒泡排序

比较相邻的元素,如果前一个元素比后一个元素的值大,就交换两个元素的位置。
在这里插入图片描述

package SortAlgorithm;

public class BubbleSort {

    /*
     * 对数组a中的元素进行排序
     * */
    public static void sort(Comparable[] a) {

        for (int i = a.length-1; i > 0; i--) {//定义冒泡的元素个数
            for (int j = 0; j < i; j++) {
                //比较索引j和索引j+1的值
                if (greater(a[j], a[j + 1])) {
                    exchange(a, j, j + 1);
                }
            }
        }

    }

    /*
     * 比较v元素是否大于w元素
     * */
    private static boolean greater(Comparable v, Comparable w) {

        return v.compareTo(w) > 0;
    }

    /*
     * 数组元素i和j交换位置
     * */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}

(2)选择排序

1 每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引处的值为最小值,最后可以找到最小值所在索引

2 交换第一个索引处和最小值所在的索引处的值
在这里插入图片描述

package SortAlgorithm;

public class SelectionSort {
    /*
     * 对数组a中的元素进行排序
     * */
    public static void sort(Comparable[] a) {
        for (int i=0;i<=a.length-2;i++){//定义选择排序的次数
            //定义一个变量,记录最小元素所在索引,默认为参与排序的第一个元素所在的位置索引
            int minIndex = i;
            for (int j=i+1;j< a.length;j++){
                //比较最小索引minIndex处的值和j索引处的值
                if (greater(a[minIndex],a[j])){
                    minIndex = j;
                }
            }
            //交换最小索引minIndex处的值和j索引处的值
            exchange(a,i,minIndex);
        }
    }

    /*
     * 比较v元素是否大于w元素
     * */
    private static boolean greater(Comparable v, Comparable w) {

        return v.compareTo(w) > 0;
    }

    /*
     * 数组元素i和j交换位置
     * */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

(3)插入排序

将所有的元素分为两组,已经排序的和未排序的;
找到未排序的组中的第一个元素,向已经排序的组中进行插入;
倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放在这个位置,其他的元素向后移动一位。
在这里插入图片描述

package SortAlgorithm;

public class InsertionSort {

    /*
     * 对数组a中的元素进行排序
     * */
    public static void sort(Comparable[] a) {

        for (int i = 1; i < a.length; i++) {//定义选择排序的元素个数
            for (int j = i; j > 0; j--) {
                //比较索引j和索引j-1的值
                if (greater(a[j - 1], a[j])) {
                    exchange(a, j - 1, j);
                } else {
                    break;
                }
            }
        }

    }

    /*
     * 比较v元素是否大于w元素
     * */
    private static boolean greater(Comparable v, Comparable w) {

        return v.compareTo(w) > 0;
    }

    /*
     * 数组元素i和j交换位置
     * */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

高级排序

(4)希尔排序

在这里插入图片描述

/
* 增长量h的确定
       int h = 1;
       while(h<数组的长度/2){
           h = 2h+1;
       }
      //循环结束后我们就可以确定h的最大值
       h的减小规则为
       h=h/2
package SortAlgorithm;

import java.util.Currency;

public class ShellSort {

    public static void sort(Comparable[] a) {

        //根据数组a的长度,确定增长量h的最大值
        int h = 1;
        while (h < a.length / 2) {
            h = 2 * h + 1;
        }

        //当增长量h小于1,排序结束
        //希尔排序
        while (h >= 1) {
            //找到待插入的元素
            for (int i = h; i < a.length; i++) {//定义待插入的元素个数
                //把待插入的元素插入到有序数列中
                for (int j = i; j >=h ; j-=h) {
                    //比较索引j和索引j-h的值
                    if (greater(a[j-h], a[j])) {
                        //交换元素
                        exchange(a, j-h, j);
                    } else {
                        //待插入元素已经找到了合适的位置,结束循环
                        break;
                    }
                }
                h=h/2;
            }
        }
    }

    /*
     * 比较v元素是否大于w元素
     * */
    private static boolean greater(Comparable v, Comparable w) {

        return v.compareTo(w) > 0;
    }

    /*
     * 数组元素i和j交换位置
     * */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

(5)归并排序

时间复杂度:O(nlog(n))
空间复杂度;因为有辅助数组,需要额外的内存开销

排序原理
在这里插入图片描述

package SortAlgorithm;

public class MergeSort {

    //归并所需要的辅助数组
    private static Comparable[] assist;

    /*
     * 比较v元素是否小于w元素
     * */
    private static boolean less(Comparable v, Comparable w) {
        return v.compareTo(w) < 0;
    }

    /*
     * 数组元素i和j交换位置
     * */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    /*
     * 对数组a中的元素进行排序
     * */
    public static void sort(Comparable[] a) {
        //1 初始化辅助数组assist
        assist = new Comparable[a.length];
        //2 定义一个lo变量和hi变量,分别记录数组中最小的索引和最大的索引
        int lo = 0;
        int hi = a.length - 1;
        //3 调用sort重载方法完成数组a中,从索引lo到索引hi的元素的排序
        sort(a, lo, hi);
    }

    /*
     * 对数组a中从lo到hi的元素进行排序
     * */
    private static void sort(Comparable[] a, int lo, int hi) {
        //安全性校验
        if (hi <= lo) {
            return;
        }
        //对lo到hi之间的数据分成两组
        int mid = lo + (hi - lo) / 2;
        //分别对每一个数组进行排序
        sort(a, lo, mid);
        sort(a, mid + 1, hi);
        //再把两个组中的数据进行归并
        merge(a, lo, mid, hi);
    }
    
    /*
     *对数组中,从0到mid为一组,从mid+1到hi为一组,对这两组数据进行归并
     * */
    private static void merge(Comparable[] a, int lo, int mid, int hi) {
        //定义三个指针
        int i = lo;
        int p1 = lo;
        int p2 = mid + 1;
        //遍历:移动p1指针和p2指针,比较对应索引处的值,找出小的那个,放在辅助数组对应的索引处
        while (p1 <= mid && p2 <= hi) {
            //比较对应索引处的值
            if (less(a[p1], a[p2])) {
                assist[i++] = a[p1++];
            } else {
                assist[i++] = a[p2++];
            }
        }
        //遍历:如果p1的指针没有走完,那么顺序移动p1指针,把对应的元素放在辅助数组的对应索引处
        while (p1 <= mid) {
            assist[i++] = a[p1++];
        }
        //遍历:如果p2的指针没有走完,那么顺序移动p2指针,把对应的元素放在辅助数组的对应索引处
        while (p2 <= hi) {
            assist[i++] = a[p2++];
        }
        //把辅助数组中的元素拷贝到原数据中
        for (int index = lo; index <= hi; index++) {
            a[index] = assist[index];
        }
    }
}

(6)快速排序

快速排序是对冒泡排序的一种改进。
时间复杂度:平均是O(nlog(n)),最坏情况是O(n²)
在这里插入图片描述
在这里插入图片描述

package SortAlgorithm;

import java.util.concurrent.CopyOnWriteArrayList;

public class QuickSort {

    /*
     * 比较v元素是否小于w元素
     * */
    private static boolean less(Comparable v, Comparable w) {
        return v.compareTo(w) < 0;
    }

    /*
     * 数组元素i和j交换位置
     * */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    /*
     * 对数组a中的元素进行排序
     * */
    public static void sort(Comparable[] a) {

        //1 定义一个low变量和high变量,分别记录数组中最小的索引和最大的索引
        int low = 0;
        int high = a.length - 1;
        //2 调用sort重载方法完成数组a中,从索引lo到索引hi的元素的排序
        sort(a, low, high);
    }


    /*
     * 对数组a中从low到high的元素进行排序
     * */
    private static void sort(Comparable[] a, int low, int high) {
        //安全性校验
        if (high <= low) {
            return;
        }
        //对lo到hi之间的数据分成两组,(左子组和右子组)
        int partition = partition(a, low, high);//返回的是分组的分界值,位置变化后的索引
        //分别对每一个数组进行排序
        sort(a, low, partition - 1);
        sort(a, partition + 1, high);
    }


    /*
     *对数组a中,从索引low到索引high之间的元素进行分组,并返回分组界对应的索引
     * */
    public static int partition(Comparable[] a, int low, int high) {
        //确定分界值
        Comparable key = a[low];
        //定义两个指针,分别指向切分元素的最小索引值和最大索引值的下一个位置
        int left = low;
        int right = high + 1;
        //初分
        while (true) {
            //先从右往左扫描,移动right指针,找到一个比分界值小的元素,停止
            while (less(key, a[--right])) {//--表示右指针向左移
                if (right == low) {
                    break;
                }
            }
            //再从左往右扫描,移动left指针,找到一个比分界值大的元素,停止
            while (less(a[++left], key)) {//++表示左指针向右移动
                if (left == high) {
                    break;
                }
            }
                //判断left>=right,如果是,则证明元素扫描完毕,结束循环,如果不是,则交换元素
                if (left >= right) {
                    break;
                } else {
                    exchange(a, left, right);
                }
            }
            exchange(a, low, right);
            return right;
        }
    
}

(7)堆排序


import java.util.Arrays;
//大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
//小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
public class MaxHeapSortAlgorithm {
    public static void main(String[] args) {
        int[] arr = {9, 5, 3, 7, 4, 8, 55,2};

        int[] res = maxHeapSort(arr);
        System.out.println(Arrays.toString(res));
    }

    public static int[] maxHeapSort(int[] arr) {
        int len = arr.length;
        buildMaxHeap(arr, len);
        for (int i=len-1;i>=0;i--){
            swap(arr,i,0);
            heapify(arr,i,0);
        }
        return arr;
    }

    public static void buildMaxHeap(int[] arr, int len) {
        int lastNode = len - 1;
        int lastParent = (lastNode - 1) / 2;
        for (int i =  lastParent; i >= 0; i--) {
            heapify(arr, len, i);
        }
    }

    //对第i个节点进行堆调整
    public static void heapify(int[] arr, int len, int i) {
        if (i >= len) {
            return;
        }
        int leftC = 2 * i + 1;
        int rightC = 2 * i + 2;
        int maxNode = i;
        if (leftC < len && arr[leftC] > arr[maxNode]) {
            maxNode = leftC;
        }
        if (rightC < len && arr[rightC] > arr[maxNode]) {
            maxNode = rightC;
        }
        if (maxNode != i) {
            swap(arr, maxNode, i);
            heapify(arr, len, maxNode);
        }
    }

    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

排序算法的稳定性
数组a中有若干个元素,其中A元素,和B 元素相等,并且A元素在B元素前面,如果使用某种排序算法排序后,能够保证A元素依然在B元素的前面,可以说这个算法是稳定的。

冒泡排序:稳定
选择排序:不稳定
插入排序:稳定

希尔排序:不稳定
归并排序:稳定
快速排序:不稳定

三 线性表

1 顺序表

在这里插入图片描述

package Linear;

//顺序表的API设计

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;

public class SequenceList<T> implements Iterable<T> {

    private T[] elements;
    private int N;

    //构造方法  //加public 是对外开放,可以继承,不加的话默认时protected ,代表只能对同包开放
    public SequenceList(int capacity) {
        //初始化数组
        this.elements = (T[]) new Object[capacity];
        //初始化长度
        this.N = 0;
    }

    //清空线性表
    public void clear() {
        this.N = 0;
    }

    //判断线性表是否为空,是返回true,否返回false
    public boolean isEmpty() {
        return N == 0;
    }

    // 获取线性表的长度
    public int length() {
        return N;

    }

    //读取并返回线性表中的第i个元素的值
    public T get(int i) {
        return elements[i];
    }


    //向线性表中添加一个元素t<br/>
    public void insert(T t) {
        if(N==elements.length){
            resize(2*elements.length);
        }
        elements[N++] = t;
    }

    //在线性表中的第i个元素之前插入一个值为t的数据元素
    public void insert(int i, T t) {
        //先把i索引处的元素及其后面的元素依次向后移动一位
        for (int index = N ; index > i; index--) {
            elements[index] = elements[index - 1];
        }
        //再把t元素放在i索引处即可
        N++;
        elements[i] = t;
    }

    //删除并返回线性表中第i个数据元素
    public T remove(int i) {
        //记录索引处的值
        T current = elements[i];
        //索引后面的元素依次向前移动一位
        for (int index = i; index < N - 1; index++) {
            elements[index] = elements[index + 1];
        }
        //元素个数减1
        N--;
        if (N<elements.length/4){
            resize(elements.length/2);
        }
        return current;
    }

    //返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
    public int indexOf(T t) {
        for (int i = 0; i < N; i++) {
            if (elements[i].equals(t)) {
                return i;
            }
        }
        return -1;
    }

    //根据参数newSize,重置elesment的大小
    public void resize(int newSize){
        //定义一个临时数组,指向原数组
        T[] temp = elements;
        //创建新数组
       elements = (T[])new Object[newSize];
        //把原数组的数据拷贝到新数组即可
        for (int i = 0; i < N; i++) {
            elements[i]=temp[i];
        }

    }


    @Override
    public Iterator<T> iterator() {
        return new SIterator();
    }

    private class SIterator implements Iterator {
        //初始化一个指针
        private int cusor;

        public SIterator() {
            this.cusor = 0;
        }

        @Override
        public boolean hasNext() {
            return cusor < N;
        }

        @Override
        public Object next() {
            return elements[cusor++];
        }
    }

}

2链表

单向链表

一个数据域,一个指针域。
链表的头结点的数据域不储存数据
在这里插入图片描述

package Linear;

import java.util.Iterator;

//链表的API设计
public class LinkList<T> implements Iterable{
    //记录头结点
    private Node head;
    //记录链表的长度
    private int N;



    //结点类
    public class Node {
        //储存数据
        T item;
        //下一个结点
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }


    //构造方法
    public LinkList() {
         //初始化头结点
         this.head=new Node(null,null);
        //初始化元素个数
         this.N=0;
    }

    //清空链表
    public void clear() {
        head.next = null;
        this.N = 0;

    }

    //判断链表是否为空,是返回true,否返回false
    public boolean isEmpty() {
        return N == 0;
    }

    // 获链表的长度
    public int length() {
        return N;
    }

    //读取并返回链表中的第i个元素的值
    public T get(int i) {
        //通过循环,从头结点开始往后找,依次找i次,就可以找到对应的元素
        Node n = head.next;
        for (int index = 0; index < i; index++) {
            n = n.next;
        }
        return n.item;
    }


    //向链表中添加一个元素t<br/>
    public void insert(T t) {
        //找到当前最后一个结点
        Node n = head;
        while (n.next != null) {
            n = n.next;
        }
        //创建新结点,保存元素t
        Node newNode = new Node(t, null);
        //让当前最后一个结点指向新结点
        n.next = newNode;
        //元素长度+1
        N++;
    }

    //在链表中的第i个元素之前插入一个值为t的数据元素
    public void insert(int i, T t) {
        //找到i位置前的一个结点
        Node pre = head;
        for (int index=0;index<i;index++){
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //创建新结点,并且新结点需要指向原来i位置的结点
        Node newNode = new Node(t,curr);
        //原来i位置的前一个结点指向新结点即可
        pre.next = newNode;
        //元素的个数+1
        N++;
    }

    //删除并返回链表中第i个数据元素
    public T remove(int i) {
        //找到i位置前的一个结点
        Node pre = head;
        for (int index=0;index<=i;index++){
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //i位置的后一个结点
        Node nextNode = curr.next;
        //前一个结点指向下一个结点
        pre.next=nextNode;
        N--;
        return curr.item;
    }

    //返回链表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
    public int indexOf(T t) {
        Node n = head;
        for (int i=0;n.next!=null;i++){
            n=n.next;
            if (n.item.equals(t)) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public Iterator<T> iterator() {
        return new LinkIterator();
    }

    private class LinkIterator implements Iterator{
        //定义一个结点
        Node n;

        public LinkIterator(){
            this.n=head;
        }

        @Override
        public boolean hasNext() {//判断是否遍历结束,返回true遍历结束,返回false遍历未结束
            return n.next!=null;
        }

        @Override
        public Object next() {//指向下一个结点
            n=n.next;
            return n.item;
        }
    }


}


双向链表

一个数据域两个指针域。

链表的头结点的数据域不储存数据,指向前驱结点的指针域为null,指向后继结点的指针域指向第一个真正储存数据的结点
在这里插入图片描述

package Linear;

import java.util.Iterator;

public class TwoWayLinkList<T> implements Iterable {
    //记录首结点
    private Node head;
    //记录最后一个结点
    private Node last;

    //记录链表的长度
    private int N;

    //结点类
    public class Node {
        public Node(T item, Node pre, Node next) {
            this.item = item;
            this.pre = pre;
            this.next = next;
        }

        //储存数据
        public T item;
        //指向上一个结点
        public Node pre;
        //指向下一个结点
        public Node next;

    }


    //构造方法
    public TwoWayLinkList() {
        //初始化头结点
        this.head = new Node(null, null, null);
        this.last = null;
        //初始化元素个数
        this.N = 0;

    }

    //清空链表
    public void clear() {

        this.head.next = null;
        this.head.pre = null;
        this.head.item = null;
        this.last = null;
        this.N = 0;

    }

    //判断链表是否为空,是返回true,否返回false
    public boolean isEmpty() {
        return this.head.next ==null;
    }

    // 获链表的长度
    public int length() {
        return N;
    }

    //获取链表中的第一个元素
    public T getFirst(){
        if (isEmpty()){
            return null;
        }
        return head.next.item;
    }

    //获取链表中的最后一个元素
    public T getLast(){
        if (isEmpty()){
            return null;
        }
        return last.item;
    }

    //读取并返回链表中的第i个元素的值
    public T get(int i) {
        //通过循环,从头结点开始往后找,依次找i次,就可以找到对应的元素
        Node n = head.next;
        for (int index = 0; index < i; index++) {
            n = n.next;
        }
        return n.item;
    }


    //向链表中添加一个元素t
    public void insert(T t) {
        //判断链表为空
        if (isEmpty()){
            //如果链表为空
            //创建新结点
            Node newNode = new Node(t,head,null);
            //让新结点成为尾结点
            last = newNode;
            //让头结点指向尾结点
            head.next=last;
        }else {
            //如果链表不为空
            Node oldlast =last;
            //创建新结点
            Node newNode = new Node(t,oldlast,null);
            //让当前的尾结点指向新结点
            oldlast.next = newNode;
            //让新结点成为尾结点
            last = newNode;
        }
        //元素个数+1
        N++;

    }

    //在链表中的第i个元素之前插入一个值为t的数据元素
    public void insert(int i, T t) {
        //找到i位置前的一个结点
        Node pre = head;
        for (int index = 0; index < i; index++) {
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //创建新结点,
        Node newNode = new Node(t,pre, curr);
        //让i位置的前一个结点的下一个结点变为新结点
        pre.next = newNode;
        //原来i位置的前一个结点指向新结点即可
        curr.pre = newNode;
        //元素的个数+1
        N++;
    }

    //删除并返回链表中第i个数据元素
    public T remove(int i) {
        //找到i位置前的一个结点
        Node pre = head;
        for (int index = 0; index <= i; index++) {
            pre = pre.next;
        }
        //找到i位置的结点
         Node curr = pre.next;
        //i位置的后一个结点
        Node nextNode = curr.next;
        //让i位置的前一个结点的下一个结点变为i位置后的下一个结点
        pre.next = nextNode;
        //让i位置的下一个结点上一个结点变为i位置前的前一个结点
        nextNode.pre=pre;

        N--;

        return curr.item;
    }

    //返回链表中首次出现的指定的数据元素的位序号,若不存在,则返回-1
    public int indexOf(T t) {
        Node n = head;
        for (int i = 0; n.next != null; i++) {
            n = n.next;
            if (n.item.equals(t)) {
                return i;
            }
        }
        return -1;
    }


    @Override
    public Iterator<T> iterator() {
        return new TWLinkIterator();
    }

    private class TWLinkIterator implements Iterator {
        //定义一个结点
        private Node n;

        public TWLinkIterator() {
            this.n = head;
        }

        @Override
        public boolean hasNext() {//判断是否遍历结束,返回true遍历结束,返回false遍历未结束
            return n.next != null;
        }

        @Override
        public Object next() {//返回下一个结点的item是多少
            n = n.next;
            return n.item;
        }
    }
    //反转整个链表
    public void reserve() {
        if (isEmpty()) {
            return ;
        }
        reserve(head.next);
    }

    //反转指定结点,并返回反转后的结点
    public Node reserve(Node curr) {
        if (curr.next == null) {
            head.next = curr;
            return curr;
        }
        //递归的去反转当前结点的下一个结点,返回值就是链表反转后的当前结点的下一个结点
        Node pre = reserve(curr.next);
        //让返回的结点的下一个结点变为当前结点curr
         pre.next =curr;
        //把当前结点的下一个结点变为null
         curr.next = null;
         return curr;
    }


}

LinkedList
底层是双向链表

链表的增(insert)删(remove)查(get)方法的时间复杂度是O(n),和线性表虽然是一样的,但是因为链表的物理地址不连续,它不需要预先指定储存空间大小,或者在存储过程中涉及到扩容等操作,同时它没有涉及元素的交换

单链表的反转
1->2->3->4
反转:4->3->2->1

//反转整个链表
    public void reserve() {
        if (isEmpty()) {
            return ;
        }
        reserve(head.next);
    }

    //反转指定结点,并返回反转后的结点
    public Node reserve(Node curr) {
        if (curr.next == null) {
            head.next = curr;
            return curr;
        }
        //递归的去反转当前结点的下一个结点,返回值就是链表反转后的当前结点的下一个结点
       // Node pre = reserve(curr.next).next=curr;
       
       //反转当前结点的下一个结点的返回值的下一个结点就是当前结点
           reserve(curr.next).next=curr;
        //让返回的结点的下一个结点变为当前结点curr
        // pre.next =curr;
        
        //把当前结点的下一个结点变为null
         curr.next = null;
         return curr;
    }

快慢指针

package test;

import Linear.LinkList;
import org.w3c.dom.Node;

public class FastSlowTest {
    public static void main(String[] args) throws Exception{
        //创建结点
       Node<String> first = new Node<String>("aa",null);
       Node<String> second = new Node<String>("bb",null);
       Node<String> third = new Node<String>("cc",null);
       Node<String> fourth = new Node<String>("dd",null);
       Node<String> fifth= new Node<String>("ee",null);
       Node<String> sixth= new Node<String>("ff",null);
       Node<String> seventh= new Node<String>("gg",null);

       //完成结点之间的指向
        first.next = second;
        second.next=third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next=sixth;
        sixth.next=seventh;

        //查找中间值
        String mid = getMid(first);
        System.out.println("中间值:"+mid);


    }

    //结点类
    public static class Node<T> {
        //储存数据
        T item;
        //下一个结点
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

    /*
    * @param first 链表的首结点
    * @return 链表的中间结点的值
    * */
    public static String getMid(Node<String> first){
        //定义两个指针
        Node<String> fast =first;
        Node<String> slow = first;
        //使用两个指针遍历链表
        while (fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow.item;
    }

}

在这里插入图片描述

有环链表入口问题
当快慢指针相遇时,我们可以判断到链表中有环,这时重新设定一个新指针指向链表的起点,且步长与慢指针一样为1,则慢指针与新指针相遇的地方就是环的入口。
在这里插入图片描述
在这里插入图片描述

package test;

import Linear.LinkList;
import org.w3c.dom.Node;

public class FastSlowTest {
    public static void main(String[] args) throws Exception {
        //创建结点
        Node<String> first = new Node<String>("aa", null);
        Node<String> second = new Node<String>("bb", null);
        Node<String> third = new Node<String>("cc", null);
        Node<String> fourth = new Node<String>("dd", null);
        Node<String> fifth = new Node<String>("ee", null);
        Node<String> sixth = new Node<String>("ff", null);
        Node<String> seventh = new Node<String>("gg", null);


        //完成结点之间的指向
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next = sixth;
        sixth.next = seventh;

        //产生环
        seventh.next = third;

        //判断链表是否有环
        boolean circle = isCircle(first);
        System.out.println("first链表是否有环:" + circle);

       /* //查找中间值
        String mid = getMid(first);
        System.out.println("中间值:" + mid);*/

        //查找链表入口
        Node entrance = getEntrance(first);
        System.out.println("有环链表入口:" + entrance.item);

    }
    /*
     * @param first 链表首结点
     * @return ture 为有环,false为无环
     * */

    public static boolean isCircle(Node<String> first) {

        //定义快慢指针
        Node<String> fast = first;
        Node<String> slow = first;
        //遍历链表,如果快慢指针最后指向同一个结点,说明链表为有环
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast.equals(slow)) {
                return true;
            }
        }
        return false;
    }


    //结点类
    public static class Node<T> {
        //储存数据
        T item;
        //下一个结点
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

    /*
     * @param first 链表的首结点
     * @return 链表的中间结点的值
     * */
    public static String getMid(Node<String> first) {
        //定义两个指针
        Node<String> fast = first;
        Node<String> slow = first;
        //使用两个指针遍历链表
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow.item;
    }

    /*
     * @param first 链表的首结点
     * @return 环的入口结点
     * */
    public static Node getEntrance(Node<String> first) {

        //定义快慢指针和临时指针
        Node<String> fast = first;
        Node<String> slow = first;
        Node<String> temp = null;

        //遍历链表,先找到环(快慢指针相遇),准备一个临时指针指向链表的首结点,直到慢指针和临时指针相遇,那么相遇时所指向的指针就是环入口
        while (fast != null && fast.next != null) {
            //变换快慢指针
            fast = fast.next.next;
            slow = slow.next;

            if (fast.equals(slow)) {
                temp = first;
                continue;
            }
            if (temp != null) {
                temp = temp.next;
                //判断临时指针是否和慢指针相遇
                if (temp.equals(slow)) {
                    break;
                }
            }
        }
        return temp;
    }
}

循环链表

在这里插入图片描述
约瑟夫问题

在这里插入图片描述

package test;

public class Joseph {

    public static void main(String[] args) {
        //解决约瑟夫问题

        //1 构建循环链表,包含41个结点
        //用来记录首结点
        Node<Integer> first = null;
        //用来记录前一个结点
        Node<Integer> pre = null;

        //遍历
        for (int i = 1; i <= 41; i++) {
            //如果是第一个结点
            if (i == 1) {
                first = new Node<>(i, null);
                pre = first;
                continue;
            }
            //如果不是第一个结点
            Node<Integer> newNode = new Node<>(i, null);
            pre.next = newNode;
            pre = newNode;

            //如果是最后一个结点
            if (i == 41) {
                pre.next = first;
            }
        }

        //2 需要count计数器,模拟报数
        int count = 0;

        //3 遍历循环链表
        //记录每次遍历的当前结点,默认时首结点
        Node<Integer> n = first;
        //记录当前结点的上一个结点
        Node<Integer> before = null;

        while (n != n.next) {
            //模拟报数
            count++;
            //判断当前报数是不是为3
            if (count == 3) {
                //如果是3,删除并打印当前结点,重置count=0,让当前结点n后移
                before.next = n.next;
                System.out.print(n.item+",");
                count = 0;
                n=n.next;
                
            } else {
                //如果不是3,让before变为当前结点,让当前结点后移
                before = n;
                n = n.next;
            }
        }

        //打印最后一个元素
        System.out.println(n.item);
    }


    //结点类
    public static class Node<T> {
        //储存数据
        T item;
        //创建下一个结点
        Node next;

        //构造方法
        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值