8 つの基本的な並べ替えアルゴリズム


はじめに: 人生において、分類せずにはいられない。例えば、体育の授業では身長で生徒を振り分けたり、学校が生徒を入学させる際には成績の高い人から低い人などで振り分けたりするが、振り分けのアルゴリズムは面接でもよく聞かれるアルゴリズムである(以下)。ソートのデフォルトは小さいものから大きいものへ)...


ソートアルゴリズムの分類

時間の複雑さに従って分類されます。

  • 時間計算量は O(n^2)
    • バブルソート
    • 選択ソート
    • 直接挿入ソート
    • ヒルソート (特別な時間計算量は O(nlogn) と O(n^2) の間です)
  • 時間計算量は O(nlogn) です
    • クイックソート
    • ヒープソート
    • マージソート
  • 時間計算量は線形オーダー O(n)
    • バケットソート
    • 基数ソート

安定性による分類:

稳定性:对于序列a中值相等的元素a[i]=a[j],i<j;排序以后的序列中a[i]、a[j]的前后位置不变则为稳定,反之则为不稳定。

  • 安定したソート: 直接挿入ソート、半選択ソート、バブルソート、マージソート、基数ソート
  • 不安定なソート: ヒープ ソート、クイック ソート、ヒル ソート、直接選択ソート

バブルソート

シーケンス内の隣接する要素のペアを比較し、最大値が右端に移動し、2 番目に大きい値が右から 2 番目に移動するまで、隣接する 2 つの要素間の最大値を徐々に移動させます...、リスクのような泡のようなもの、大きい方の泡を上に浮かせます。

ここに画像の説明を挿入します

//todo 1.冒泡排序
    public static void maopao(int[] arr){
    
    
        int len=arr.length;
        for (int i=1;i<len;i++){
    
    
            boolean is_sort=true;  //假设有序
            for (int j=0;j<len-i;j++){
    
    
                if (arr[j]>arr[j+1]){
    
     //交换相邻元素
                    arr[j]=arr[j]^arr[j+1];
                    arr[j+1]=arr[j]^arr[j+1];
                    arr[j]=arr[j]^arr[j+1];
                    is_sort=false;
                }
            }
            if (is_sort){
    
     //如果这一轮没有交换说明已经有序了,直接退出
                break;
            }
        }
    }

選択ソート

アイデア: 各ラウンドで最小要素のインデックスを選択し、最小要素のインデックスを左の要素と直接交換します。このソートの最大の利点は、バブル ソートに似た複数の交換を排除できることです。

//todo 2.选择排序
    public static void change(int[] arr){
    
    
        int len=arr.length;
        for (int i=0;i<len;i++){
    
    
            int minIndex=i; //选定最小元素索引
            for (int j=i+1;j<len;j++){
    
    
                if (arr[j]<arr[minIndex])
                    minIndex=j;
            }
            if (minIndex != i){
    
     
                arr[minIndex]=arr[minIndex]^arr[i];
                arr[i]=arr[minIndex]^arr[i];
                arr[minIndex]=arr[minIndex]^arr[i];
            }
        }
    }

直接挿入ソート

アイデア: 順序付けされた領域を維持し、配列のすべての要素が順序付けされるまで、配列内の要素を順序付けされた領域内の適切な位置に 1 つずつ挿入します。
ここに画像の説明を挿入します

//todo 3.直接插入排序
    public  static void insert_sort(int[] arr){
    
    
        int len=arr.length;
        for (int i=1;i<len;i++){
    
    
            int j=i-1;
            int tmp=arr[i]; //将当前元素拷贝,用以与之前元素比较插入
            while (j>=0 && tmp<arr[j]){
    
    
                arr[j+1]=arr[j]; //将较大元素往后移动,以备当前元素插入
                j--; //逐步向之前元素进行比较
            }
            arr[j+1]=tmp;
        }
    }

クイックソート

アイデア: 各ラウンドでベンチマーク要素ピボットを選択し、それより大きい他の要素をピボットの右側に移動し、小さい要素を左側に移動して、配列を 2 つの部分に分割し、その後、2 つの部分を再帰的に並べ替えます。 - -分割統治。

//todo 4.快速排序
    public static void quick_sort(int[] arr,int left,int right){
    
    
        if (left>right)
            return;
        int i=left;
        int j=right;
        int pivot=arr[left];
        while (i<j){
    
    
            while (i<j && arr[j]>pivot)
                j--;
            while (i<j && arr[i]<=pivot)
                i++;
            if (i<j){
    
    
                arr[i]=arr[i]^arr[j];
                arr[j]=arr[i]^arr[j];
                arr[i]=arr[i]^arr[j];
            }
        }
        //将基准元素与重合元素交换
        arr[left]=arr[i];
        arr[i]=pivot;
        quick_sort(arr,left,i-1); //将左边部分递归排序
        quick_sort(arr,i+1,right); //将右边部分递归排序
    }

マージソート

アイデア: マージ ソートは、シーケンスを短いシーケンス (グループ) に再帰的に分割することです。再帰的な終了とは、短いシーケンスに 1 つの要素 (直接順序付けされていると見なされる) または 2 つのシーケンス (1 つの比較と交換) のみが含まれ、その後、それぞれが順序付けされることです。セグメントは、シーケンスが長い順序のシーケンスにマージされ (マージ)、元のシーケンスがすべてソートされるまで継続的にマージされます。
ここに画像の説明を挿入します

//todo 5.归并排序
    //5.1 数组分组
    public static void split_arr(int[] arr,int left,int right){
    
    
        if (left < right){
    
    
            int middle=left+(right-left)/2; //防止(right+left)/2 数据溢出
            //左边分组
            split_arr(arr,left,middle);
            //右边分组
            split_arr(arr,middle+1,right);
            //分组合并排序
            merge_arr_sort(arr,left,middle,right);
        }
    }
    //5.2 数组合并排序
    public static void merge_arr_sort(int[] arr,int left,int middle,int right){
    
    
        int[] tmp=new int[right-left+1]; //存储合并的数组
        int i=left;
        int j=middle+1;
        int t=0;
        while (i<=middle && j<=right){
    
    
            if (arr[i]<arr[j]){
    
    
                tmp[t++]=arr[i++];
            }else tmp[t++]=arr[j++];
        }
        while (i<=middle) //当左边部分还有剩余元素(剩余元素因为之前的排序已经有序)时直接插入临时数组
            tmp[t++]=arr[i++];
        while (j<=right) //当右边部分还有剩余元素时直接插入临时数组
            tmp[t++]=arr[j++];
        //将合并的数组拷贝到原数组中
        System.arraycopy(tmp,0,arr,left,tmp.length);
    }

ヒルソート

アイデア: 配列内のほとんどの要素が順序どおりであれば、挿入ソートの作業負荷は比較的小さく、配列内の要素を頻繁に比較したり交換したりする必要はありません。
ここに画像の説明を挿入します

//todo 6.希尔排序
    public static void shell_sort(int[] arr){
    
    
        int len=arr.length;
        for (int d=len/2;d>0;d/=2){
    
     //d:增量
            for (int i=d;i<len;i++){
    
    
                int j=i-d;
                int tmp=arr[i];
                while (j>=0 && tmp<arr[j]){
    
    
                    arr[j+d]=arr[j];
                    j -= d;
                }
                arr[j+d]=tmp;
            }
        }
    }

バケットソート

アイデア: 配列を限られた数のバケットに分割します。次に、各バケットが個別に並べ替えられ (他の並べ替えアルゴリズムを使用することも、再帰的な方法でバケットの並べ替えを使用し続けることも可能です)、最後に、順序付けされたシーケンスを記憶するために各バケット内のレコードがリストされます。

//todo 7.桶排序
    public static int[] bucket_sort(int[] arr){
    
    
        int len=arr.length;
        //1.求极值
        int max=arr[0];
        int min=arr[0];
        for (int i=1;i<len;i++){
    
    
            if (arr[i]<min)
                min=arr[i];
            if (arr[i]>max)
                max=arr[i];
        }
        int d=max-min;
        //2.对桶进行初始化
        ArrayList<LinkedList<Integer>> buckets = new ArrayList<>(len);
        for (int i=0;i<len;i++){
    
    
            buckets.add(new LinkedList<>());
        }
        //3.对数组中元素存放到对应桶中(将桶中元素映射到0~len-1的索引中)
        for (int x:arr){
    
    
            int index=(int)((x-min)/d*(len-1));
            buckets.get(index).add(x);
        }
        //4将各个桶中所有元素排序
        for (int i=0;i<len;i++){
    
    
            Collections.sort(buckets.get(i));
        }
        //5输出各元素
        int[] result = new int[len];
        int t=0;
        for (int i=0;i<len;i++){
    
    
            for (int j=0;j<buckets.get(i).size();j++)
                result[t++]=buckets.get(i).get(j);
        }
        return result;
    }

ヒープソート

ヒープは完全二分木と呼ばれるデータ構造であり、大きなルートヒープと小さなルートヒープに分けることができ、ヒープソートはこの構造に基づいたプログラムアルゴリズムです。
ここに画像の説明を挿入します

//todo 8.堆排序
    /*
    * @param arr 要维护的数组
    * @param parentIndex 要维护的节点
    * @param len 要维护的数组长度
    * */
    public static void heapify(int[] arr,int parentIndex,int len){
    
    
        //计算左右子节点
        int leftChildren = 2*parentIndex+1;
        int rightChildren = leftChildren+1;
        //求最大节点的索引
        int largest = parentIndex;
        if (leftChildren<len && arr[leftChildren]>arr[largest])
            largest = leftChildren;
        if (rightChildren<len && arr[rightChildren]>arr[largest])
            largest = rightChildren;
        if (largest != parentIndex){
    
    
            arr[largest]=arr[largest]^arr[parentIndex];
            arr[parentIndex]=arr[largest]^arr[parentIndex];
            arr[largest]=arr[largest]^arr[parentIndex];
            //维护parentIndex节点
            heapify(arr,largest,len);
        }
    }
    //创建最大堆
    public static void heap_sort(int[] arr){
    
    
        int len=arr.length;
        for (int i=len/2-1;i>=0;i--){
    
    
        //最后一个节点索引为len-1,其对应的父节点带入(n-1)/2即得:len/2-1
            //从最后一个父节点开始维护最大堆性质
            heapify(arr,i,len);
        }
        //选择最大元素进行交换
        for (int i=len-1;i>0;i--){
    
    
            arr[i]=arr[i]^arr[0];
            arr[0]=arr[i]^arr[0];
            arr[i]=arr[i]^arr[0];
            //将互换以后的堆顶维护性质
            heapify(arr,0,i);
        }
    }

インタビュー 1:
ファイルがあります。ファイルの各行は書籍情報データです。カンマ (,) で 4 つの部分に分かれています。形式は次のとおりです。id は
id,category,words,updatetime
書籍 ID を表し、long 型で、id は非形式です。繰り返し;
c​​ategory は書籍の分類を表します (int 型)。すべてのデータの分類には
書籍内の単語数を表す数単語しか含まれていないことに注意してください。また、int 型の
updatetime は書籍の更新時間を表します. 形式は 2020-02-01 23:00:00 です。

4
66,20002,25919,2020-02-16 17:35:00
63,20004,9914,2020-02-16 17:35:00
60,20001,1982,2020-02-16 17:35:00
68,20004,1693,2020-02-16 17:35:00

ファイルデータをソートしてIDを出力するプログラムを作成してください(ソート優先順位はカテゴリ>更新時刻>単語>IDの昇順でソートします)。

public class Demo3 {
    
    
   
        public static void main(String[] args) throws Exception {
    
    
            //时间转换格式
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            ArrayList<Book> books = new ArrayList<>();
            //获取文件数据流
            File file = new File("G:/t1.txt");
            try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
    
    
                List<String> list =bufferedReader.lines()  //bufferedreader读取文件数据
                        .filter(e -> e.length() > 4)
                        .collect(Collectors.toList());
                for (String e:list){
    
    
                    String[] fields = e.split(",");
                    books.add(new Book(Long.parseLong(fields[0]),Integer.parseInt(fields[1]),
                            Integer.parseInt(fields[2]),dateFormat.parse(fields[3]).getTime()));
                }
                books.sort(new Comparator<Book>() {
    
    
                    @Override
                    public int compare(Book o1, Book o2) {
    
    
                        if (o1.getCategory()!=o2.getCategory()){
    
    
                            return o1.getCategory()-o2.getCategory();
                        }else if (o1.getUpdatetime()!= o2.getUpdatetime()){
    
    
                            return (int) (o1.getUpdatetime()-o2.getUpdatetime());
                        }else if (o1.getWords()!=o2.getWords()){
    
    
                            return o1.getWords()-o2.getWords();
                        }else return (int) (o1.getId()-o2.getId());
                    }
                });
                books.forEach(e->{
    
    
                    System.out.println(e.getId());
                });
                // 结果:60 66 68 63
            }
        }

     static class Book{
    
      //将文件中的数据封装成书籍类
            private long id;
            private int category;
            private int words;
            private long updatetime;
            //todo 省略set、get方法,无参有参方法
     }
}

インタビュー 2: 2 つの順序付き配列を新しい順序付き配列にマージする

public class test2 {
    
    

    public static void main(String[] args) {
    
    
      // 合并两个有序数组,成为一个新的有序数组
        int[] arr1 = {
    
    10,18,22,40,56};
        int[] arr2 = {
    
    7,12,32,50,54};
        System.out.println("合并以后的数组为:"+Arrays.toString(merge_arr(arr1,arr2)));
    }

    //参考的归并排序中的归并操作
    private static int[] merge_arr(int[] arr1, int[] arr2) {
    
    
        //创建临时数组用以存储合并两个数组的元素
        int[] tmp = new int[arr1.length + arr2.length];
        int t=0;
        int i=0,j=0;
        while (i<arr1.length && j<arr2.length){
    
    
            if (arr1[i]<arr2[j]){
    
    
                tmp[t++]=arr1[i++];
            }else tmp[t++]=arr2[j++];
        }
        while (i<arr1.length)
            tmp[t++]=arr1[i++];
        while (j<arr2.length)
            tmp[t++]=arr2[j++];
        return tmp;
    }
}

インタビュー 3: N 個の整数の配列には複数の奇数と偶数が存在します。すべての奇数が左側に来るようにソート アルゴリズムを設計します。

	public static void get_result2(int[] arr){
    
    
        //设置两个指针p1,p2指向头和尾,直到p1为偶数,p2为奇数时交换两指针的值(参考了快速排序)
        int p1=0,p2=arr.length-1;
        while (p1<p2){
    
    
            while ((arr[p1]&1)!=0)
                p1++;
            while ((arr[p2]&1)==0)
                p2--;
            if (p1<p2){
    
    
                arr[p1]=arr[p1]^arr[p2];
                arr[p2]=arr[p1]^arr[p2];
                arr[p1]=arr[p1]^arr[p2];
            }
        }
    }

インタビュー: リンクされたリストの作成、削除、並べ替え、逆順などの操作

public class test7 {
    
    

    //todo size标记链表长度(节点个数)
    private static int size=0;

    public static void main(String[] args) {
    
    
        //创建链表
        Node head = create_link(5);
        show_link(head);
        System.out.println("获取第2个节点"+get_index(2+1,head));
        System.out.println("向链表第三个位置插入333:");
        insert(3, 333, head);
        show_link(head);
        System.out.println("向链表头部插入0:");
        head = insert(1, 0, head);
        show_link(head);
        System.out.println("向链表尾部插入999:");
        head = insert(size, 999, head);
        show_link(head);
        System.out.println("删除头部节点:");
        head = delete_index(1, head); //删除头部节点
        show_link(head);
        System.out.println("删除尾部节点:");
        head = delete_index(size, head);
        show_link(head);
        System.out.println("删除第三个节点:");
        head = delete_index(3, head);
        show_link(head);
        System.out.println("链表反转后:");
        head = recursion_reverse(head);
        show_link(head);
        System.out.println("排序以后的链表:");
        show_link(maopao_sort(head));
    }

    //todo 向链表中第index个位置插入element元素
    public static Node insert(int index,int element,Node head){
    
    
        if (index<=0 || index>size){
    
    
            throw new IndexOutOfBoundsException("插入位置有误!");
        }
        if (index==1){
    
      //插入头部
            Node current = new Node(element);
            current.next=head;
            head=current;
        }else if (index==size){
    
      //插入尾部
            Node pre_last = get_index(index, head);
            pre_last.next.next=new Node(element);
        }else {
    
      //插入中间部分
            //先获取第index-1的节点
            Node pre_index = get_index(index, head);
            Node current = new Node(element);
            current.next=pre_index.next;
            pre_index.next=current;
        }
        size++;
        return head;
    }

    //TODO 头插法创建链表
    public static Node create_link(int n){
    
    
        size=n;
        Node head=null;
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入链表data:");
        for (int i=0;i<n;i++){
    
    
            if (i==0){
    
    
                head=new Node(scanner.nextInt());
            }else {
    
    
                Node current=new Node(scanner.nextInt());
                current.next=head;
                head=current;
            }
        }
        return head;
    }

    //todo 尾插法创建链表
    public static Node create_link2(int n){
    
    
        size=n;
        Node head=null;
        Node tmp=null;
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入链表data:");
        for (int i = 0; i < n; i++) {
    
    
            if (i==0){
    
    
                head=new Node(scanner.nextInt());
                head.next=null;
                tmp=head;
            }else{
    
    
                Node current = new Node(scanner.nextInt());
                tmp.next=current;
                tmp=current;
                current.next=null;
            }
        }
        return head;
    }

    //todo 展示链表所有节点数据
    public static void show_link(Node head){
    
    
        Node tmp=head;
        System.out.print("链表所有数据:");
        while (tmp!=null){
    
    
            System.out.print(tmp.data+" ");
            tmp=tmp.next;
        }
        System.out.println();
    }

    //todo 获取index-1的节点数据
    public static Node get_index(int index,Node head){
    
    
        Node tmp=head;
        for (int i =1; i < index-1; i++) {
    
    
            tmp=tmp.next;
        }
        return tmp;
    }

    //todo 删除第index个节点
    public static Node delete_index(int index,Node head){
    
    
        if (index<=0 || index>size){
    
    
            throw new IndexOutOfBoundsException("删除位置有误!");
        }
        if (index==1){
    
      //删除头节点
            head= head.next;
        }else if (index==size){
    
     //删除尾结点
            Node pre_index = get_index(index, head);
            pre_index.next=null;
        }else {
    
      //删除中间节点
            Node pre_index = get_index(index, head);
            pre_index.next=pre_index.next.next;
        }
        size--;
        return head;
    }

    //TODO 使用冒泡排序对链表进行排序
    public static Node maopao_sort(Node head){
    
    
        Node new_head=head;
        for (int i=1;i<size;i++){
    
    
            Node pre=null;
            Node current=new_head;
            Node N=current.next;
            for (int j=0;j<size-i;j++){
    
    
                if (current.data>N.data){
    
    
                    current.next=N.next;
                    N.next=current;
                    if (pre==null){
    
    
                        new_head=N;
                    }else{
    
    
                        pre.next=N;
                    }
                    pre=N;
                    N=current.next;
                }else {
    
    
                    pre=current;
                    current=N;
                    N=N.next;
                }
            }
        }
        return new_head;
    }

    //todo 链表反转
    public static Node reverse_link(Node head){
    
    
        Node new_head=null;
        Node tmp=head;
        Node Next=head;
        while (Next!=null){
    
    
           Next=Next.next;
           tmp.next=new_head;
           new_head=tmp;
           tmp=Next;
        }
        return new_head;
    }

    //todo 链表递归反转
    public static Node recursion_reverse(Node head){
    
    
        if (head.next==null)
            return head;
        Node new_head = recursion_reverse(head.next);
        head.next.next=head;
        head.next=null;
        return new_head;
    }

    //链表节点对象
    static class Node{
    
    

        private int data; //数据域
        private Node next; //指针域

        public Node(int data){
    
    
            this.data=data;
        }

        @Override
        public String toString() {
    
    
            return "Node{" +
                    "data=" + data +
                    ", next=" + next +
                    '}';
        }
    }
}

インタビュー: リンク リスト内の val に等しいすべてのノードを削除します。

 //todo  删除链表中等于给定值val的所有节点
    public static Node deleteByVal(Node head,int val){
    
    
        Node tmp=head;
        int index=0;
        while (tmp!=null){
    
    
            index++;
            if (tmp.data==val){
    
    
               delete_index(index,head);
                index--;
            }
            tmp=tmp.next;
        }
        return head;
    }
//todo  删除链表中等于给定值val的所有节点
    public static Node removeVal(Node head,int val){
    
    
        if (head==null){
    
    
            return null;
        }
        head.next = removeVal(head.next, val);
        return head.data==val?head.next:head;
    }

再帰的ソリューションの詳細な説明:
ここに画像の説明を挿入します
インタビュー: バイナリ ツリーの作成とトラバーサル

package algorithm_;
import java.util.*;
/**
 * @author brett
 * @date 2022-12-10 20:05:17
 */
public class BinaryTree {
    
    

    public static void main(String[] args) {
    
    
        LinkedList<Integer> list = new LinkedList<>(
                Arrays.asList(8,7,6,null,null,5,null,null,9,4,1,null,null,null,3,10)
        );
        TreeNode root = createTree(list);
//        System.out.println("前序遍历...");
//        preOrder(root);
//        System.out.println("中序遍历...");
//        inOrder(root);
//        System.out.println("非递归前序遍历...");
//        preOrder2(root);
//        System.out.println("非递归中序遍历...");
//        inOrder2(root);
//        System.out.println("后序遍历...");
//        postOrder(root);
//        System.out.println("非递归中序遍历...");
//        postOrder2(root);
        System.out.println("非递归层序遍历...");
        levelOrder(root);
        System.out.println("递归层序遍历...");
        levelOrder2(Collections.singletonList(root));
    }

    //递归--创建二叉树
    public static TreeNode createTree(LinkedList<Integer> list){
    
    
        TreeNode node=null;
        if (list==null || list.isEmpty()){
    
    
            return null;
        }
        Integer data = list.removeFirst();
        if (data!=null){
    
    
            node=new TreeNode(data);
            node.left=createTree(list);
            node.right=createTree(list);
        }
        return node;
    }

    //前序遍历
    public static void preOrder(TreeNode node){
    
    
        if (node==null){
    
    
            return;
        }
        System.out.println(node.data);
        preOrder(node.left);
        preOrder(node.right);
    }

    //中序遍历
    public static void inOrder(TreeNode node){
    
    
        if (node==null){
    
    
            return;
        }
        inOrder(node.left);
        System.out.println(node.data);
        inOrder(node.right);
    }

    //后序遍历
    public static void postOrder(TreeNode node){
    
    
        if (node==null){
    
    
            return;
        }
        postOrder(node.left);
        postOrder(node.right);
        System.out.println(node.data);
    }

    //非递归前序遍历
    public static void preOrder2(TreeNode root){
    
    
        Stack_Tree stackTree = new Stack_Tree(10);
        TreeNode tmp=root;
        while (tmp!=null || !stackTree.isEmpty()){
    
    
            while (tmp!=null){
    
    
                System.out.println(tmp.data);
                stackTree.push(tmp);
                tmp=tmp.left;
            }
            if (!stackTree.isEmpty()){
    
    
                tmp=stackTree.pop();
                tmp=tmp.right;
            }
        }
    }
    //非递归中序遍历
    public static void inOrder2(TreeNode root){
    
    
        Stack_Tree stackTree = new Stack_Tree(10);
        TreeNode tmp=root;
        while (tmp!=null || !stackTree.isEmpty()){
    
    
            while (tmp!=null){
    
    
                stackTree.push(tmp);
                tmp=tmp.left;
            }
            if (!stackTree.isEmpty()){
    
    
                tmp=stackTree.pop();
                System.out.println(tmp.data);
                tmp=tmp.right;
            }
        }
    }
    //非递归后序遍历
    public static void postOrder2(TreeNode root){
    
    
        Stack_Tree stackTree = new Stack_Tree(10);
        TreeNode tmp=root;  //当前访问的节点
        TreeNode lastVisit=null;  //标记上一次访问的节点
        while (tmp!=null){
    
    
            stackTree.push(tmp);
            tmp=tmp.left;
        }
        while (!stackTree.isEmpty()) {
    
    
            tmp=stackTree.pop();//弹出栈顶元素
            //一个根节点被访问的前提是:无右子树或右子树已被访问过
            if (tmp.right!=null && tmp.right!=lastVisit){
    
    
                //根节点再次入栈
                stackTree.push(tmp);
                //进入右子树,且可以肯定右子树一定不为空
                tmp=tmp.right;
                while (tmp!=null){
    
    
                    //再走到左子树最左边
                    stackTree.push(tmp);
                    tmp=tmp.left;
                }
            }else {
    
    
                //访问
                System.out.println(tmp.data);
                //修改最近被访问的节点
                lastVisit = tmp;
            }
        }
    }

    //层序遍历
    public static void levelOrder(TreeNode root){
    
    
        Queue queue = new Queue(10);
        queue.enQueue(root);
        while (!queue.isEmpty()){
    
    
            TreeNode tmp = queue.deQueue();
            System.out.println(tmp.data);
            if (tmp.left!=null){
    
    
                queue.enQueue(tmp.left);
            }
            if (tmp.right!=null){
    
    
                queue.enQueue(tmp.right);
            }
        }
    }

    //层序遍历递归实现
    public static void levelOrder2(List<TreeNode> list){
    
    
        if (list.size()==0)
            return;
        List<TreeNode> list2=new ArrayList<>();
        for (TreeNode e:list) {
    
    
            System.out.println(e.data);
            if (e.left!=null){
    
    
                list2.add(e.left);
            }
            if (e.right!=null){
    
    
                list2.add(e.right);
            }
        }
        levelOrder2(list2);
    }

    static class TreeNode{
    
    
        //数据域
        public int data;
        //左子树
        public TreeNode left;
        //右子树
        public TreeNode right;

        public TreeNode(int data){
    
    
            this.data=data;
        }
    }
    static class Stack_Tree{
    
    
        //数据域
        public TreeNode[] arr;
        //栈有效长度
        public int size;

        public Stack_Tree(int capacity){
    
    
            arr=new TreeNode[capacity];
            this.size=0;
        }
        //出栈
        public TreeNode pop(){
    
    
            if (this.size==0){
    
    
                throw new IndexOutOfBoundsException("栈空");
            }
            TreeNode remove_data = this.arr[size - 1];
            this.size--;
            return remove_data;
        }
        //入栈
        public void push(TreeNode node){
    
    
            if (this.size==this.arr.length){
    
    
                throw new IndexOutOfBoundsException("栈满");
            }
            this.arr[this.size]=node;
            this.size++;
        }
        //判断栈是否为空
        public boolean isEmpty(){
    
    
            return this.size == 0;
        }
    }
    static class Queue{
    
    
        TreeNode[] arr;  //存储数据
        int front;  //头指针
        int rear;  //尾指针

        public Queue(int capacity){
    
    
            arr=new TreeNode[capacity];
        }

        //入队操作
        public void enQueue(TreeNode node){
    
    
            //判断是否队满
            if ((rear+1)%this.arr.length==front){
    
    
                throw new IndexOutOfBoundsException("队列已经满了");
            }
            this.arr[rear]=node;
            rear=(rear+1)%this.arr.length;
        }
        //出队操作
        public TreeNode deQueue(){
    
    
            //判断是否对空
            if (rear==front){
    
    
                throw new IndexOutOfBoundsException("队列已经空了");
            }
            TreeNode remove_data=this.arr[front];
            front=(front+1)%this.arr.length;
            return remove_data;
        }
        //输出队列中所有元素
        public void output(){
    
    
            for (int i=front;i!=rear;i=(i+1)%this.arr.length){
    
    
                System.out.println(this.arr[i]);
            }
        }
        //判断队空
        public boolean isEmpty(){
    
    
            return rear==front;
        }
    }
}

おすすめ

転載: blog.csdn.net/qq_49472679/article/details/128200922