手撕代码

参考:https://blog.csdn.net/ryjflyshy/article/details/78250348

【1】二叉树前序遍历的非递归实现

树结构

//树的节点类
class Node {
	public int val; //节点值
	public Node left; //左子树
	public Node right; //右子树
	public Node() {}
	public Node(int val, Node left, Node right) {
		this.val = val;
		this.left = left;
		this.right = right;
	}
}

     * 实现思路,先序遍历是要先访问根节点,然后再去访问左子树以及右子树,这明显是递归定义,但这里是用栈来实现的
     * 首先需要先从栈顶取出节点,然后访问该节点,如果该节点不为空,则访问该节点,同时把该节点的右子树先入栈,然后

     * 左子树入栈。循环结束的条件是栈中不在有节点。即 !s.empty()

public void preOrder(Node root) {
	Stack<Node> s = new Stack<Node>();
	s.push(root);
	Node p = null;
	while (!s.empty()) {
		p = s.pop();
		if (p != null) {
			System.out.print(p.val+" ");
			s.push(p.right);
			s.push(p.left);
		}
	}
}
【2】二叉树的中序遍历非递归实现

     * 实现思路,中序遍历是要先遍历左子树,然后跟节点,最后遍历右子树。所以需要先把跟节点入栈然后在一直把左子树入栈
     * 直到左子树为空,此时停止入栈。栈顶节点就是我们需要访问的节点,取栈顶节点p并访问。然后改节点可能有右子树,所以
     * 访问完节点p后还要判断p的右子树是否为空,如果为空则接下来要访问的节点在栈顶,所以将p赋值为null。如果不为空则
     * 将p赋值为其右子树的值。 循环结束的条件是p不为空或者栈不为空。

public void inOrder(Node root) {
      Stack<Node> s = new Stack<Node>();
      Node p = root;
      do {
          while (p != null) {
             s.push(p);
             p = p.left;
          }
          p = s.pop();
          System.out.print(p.val+" ");
          if (p.right != null) {
              p = p.right;
          }
          else p = null;
     } while(p != null || !s.empty());
}

【3】二叉树的后序遍历里非递归实现

     * 实现思路,在进行后序遍历的时候是先要遍历左子树,然后在遍历右子树,最后才遍历根节点。所以在非递归的实现中要先把根节点入栈然后再把左子树入栈直到左子树为空,此时停止入栈。此时栈顶就是需要访问的元素,所以直接取出访问p。在访问结束后,还要判断被访问的节点p是否为栈顶节点的左子树,如果是的话那么还需要访问栈顶节点的右子树,所以将栈顶节点的右子树取出赋值给p。如果不是的话则说明栈顶节点的右子树已经访问完了,那么现在可以访问栈顶节点了,所以此时将p赋值为null。判断结束的条件是p不为空或者栈不为空,若果两个条件都不满足的话,说明所有节点都已经访问完成。

 public void postOrder(Node root) {
		
		Stack<Node> s = new Stack<Node>();
		Node p = root;
		while (p != null || !s.empty()) {
			while(p != null) {
				s.push(p);
				p = p.left;
			}
			p = s.pop();
			System.out.print(p.val+" ");
			//这里需要判断一下,当前p是否为栈顶的左子树,如果是的话那么还需要先访问右子树才能访问根节点
			//如果已经是不是左子树的话,那么说明左右子书都已经访问完毕,可以访问根节点了,所以讲p复制为NULL
			//取根节点
			if (!s.empty() && p == s.peek().left) {
				p = s.peek().right;
			}
			else p = null;
		}
	}

【4】二叉树遍历递归实现


    //前序遍历,根=》左=》右
    public void preorder_Traversal(TreeNode root)
    {
        if(root==null)return;
        
        //访问节点的逻辑代码块
        System.out.print(root.val+" ");
        
        preorder_Traversal(root.left);
        preorder_Traversal(root.right);
    }

    //中序遍历,左=》根=》右
    public void inorder_Traversal(TreeNode root)
    {
        if(root==null)return;
        inorder_Traversal(root.left);
        
         //访问节点的逻辑代码块
        System.out.print(root.val+" ");
        
        inorder_Traversal(root.right);
    }

    //后序遍历,左=》右=》根
    public void postorder_Traversal(TreeNode root)
    {
        if(root==null)return;
        postorder_Traversal(root.left);
        postorder_Traversal(root.right);
        
         //访问节点的逻辑代码块
        System.out.print(root.val+" ");
    }

【5】二分查找非递归版

public static int binarySearch(int a[], int target) {
        if (a == null || a.length == 0) return -1;
        int left = 0, right = a.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (a[mid] == target) return mid;
            else if (a[mid] > target) right = mid - 1;
            else left = mid + 1;
        }
        return -1;
    }

【6】二分查找递归版

public static int BinSearch(int[] arr, int low, int high, int key)    
{    
    if (low>high) return -1;
    int mid = (low+high)/2;    
    if(key == Array[mid])    
        return mid;    
    else if(key<Array[mid])    
        return BinSearch(arr,low,mid-1,key);    
    else if(key>Array[mid])    
        return BinSearch(arr,mid+1,high,key);  
} 

【7】排序相关:

  平均复杂度 最好复杂度 最坏复杂度 辅助空间 稳定性 备注
冒泡排序 O\left ( n^{2} \right ) O\left ( n \right ) O\left ( n^{2} \right ) O\left ( 1 \right ) 稳定 对于长度较短的序列,效果更好
简单选择排序 O\left ( n^{2} \right ) O\left ( n^{2} \right ) O\left ( n^{2} \right ) O\left ( 1 \right ) 不稳定 对于长度较短的序列,效果更好
直接插入排序 O\left ( n^{2} \right ) O\left ( n \right ) O\left ( n^{2} \right ) O\left ( 1 \right ) 稳定 当大部分有序,适用
希尔排序 O\left ( n\lg n \right )O\left ( n^{2} \right ) O\left ( n^{1.3} \right ) O\left ( n^{2} \right ) O\left ( 1 \right ) 不稳定  
堆排序 O\left ( n\lg n \right ) O\left ( n\lg n \right ) O\left ( n\lg n \right ) O\left ( 1 \right ) 不稳定 堆是完全二叉树,topN用小顶堆,lowN用大顶堆
归并排序 O\left ( n\lg n \right ) O\left ( n\lg n \right ) O\left ( n\lg n \right ) O\left ( n \right ) 稳定 n大好,n个元素k路归并排序次数S=logk(m)
快速排序 O\left ( n\lg n \right ) O\left ( n\lg n \right ) O\left ( n^{2} \right ) O\left ( 1 \right ) 不稳定 n大好

(1)冒泡排序

冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

(2)选择排序

选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n - 1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

(3)插入排序 
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

(4)快速排序 
快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j,交换a[i]和a[j],重复上面的过程,直到i > j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。

(5)归并排序 
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

(6)基数排序 
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。

基数排序的一个应用是将字符串排序,如果所有字符都有同样的长度L,则对每个字符使用桶,可以实现O(NL)时间内的基数排序。

(7)希尔排序(shell) 
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小, 插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比O(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

(8)堆排序 
我们知道堆的结构是节点i的孩子为2 * i和2 * i + 1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程是从第n / 2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n / 2 - 1, n / 2 - 2, ... 1这些个父节点选择元素时,就会破坏稳定性。有可能第n / 2个父节点交换把后面一个元素交换过去了,而第n / 2 - 1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。

(9)桶排序:为了使桶排序能够正常工作,必须添加一些附加条件,输入数据A1,A2,...,AN必须由小于M的正整数组成,那么使用一个大小为M称为count的数组,初始化全为0,当读入Ai时,就让count[Ai]增加1,在所有输入数据被读入后,扫描count数组,打印出排序表,时间复杂度为O(N)。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BasicSort {

    /**
     * 冒泡排序
     */
    public static <T extends Comparable<? super T>> void bubbleSort(T[] arr){
        boolean flag = true;
        for (int i=0;i<arr.length & flag;i++){
            for (int j=arr.length-1;j>=i;j--){
                if (arr[j].compareTo(arr[j-1])<0){
                    T temp = arr[j-1];
                    arr[j-1] = arr[j];
                    arr[j] = temp;

                    flag=false;
                }
            }
        }

        System.out.println(Arrays.toString(arr));
    }

    /**
     * 简单选择排序
     */
    public static <T extends Comparable<? super T>> void simpleSort(T[] arr){
        for (int i=0;i<arr.length;i++){
            int min = i;
            for(int j=i;j<arr.length;j++){
                if (arr[min].compareTo(arr[j])>0){
                    min = j;
                }
            }
            if (min!=i){
                T temp = arr[i];
                arr[min] = arr[i];
                arr[i] = temp;
            }
        }

        System.out.println(Arrays.toString(arr));
    }

    /**
     * 插入排序
     */
    public static <T extends Comparable<? super T>> void  insertSort(T[] arr){
        for (int i=1;i<arr.length;i++){
            T temp = arr[i];
            int j ;
            for (j=i;j>0 & temp.compareTo(arr[j-1])<0;j--){
                arr[j] = arr[j-1];
            }
            arr[j] = temp;
        }
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 希尔排序
     */
    public static <T extends Comparable<? super T>> void shellSort(T[] arr){
        for (int gap=arr.length/2;gap>=1;gap=gap/2){
            for(int i=gap;i<arr.length;i++){
                T temp = arr[i];
                int j ;
                for(j=i;j>=gap & temp.compareTo(arr[j-gap])<0;j=j-gap){
                    arr[j] = arr[j-gap];
                }

                arr[j] = temp;
            }
        }
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 归并排序
     */
    public static <T extends Comparable<? super T>> void mergeSort(T[] arr,T[] tempArr,int left,int right){
        if (left<right){
            int mid = (left+right)/2;
            mergeSort(arr,tempArr,left,mid);
            mergeSort(arr,tempArr,mid+1,right);
            merge(arr,tempArr,left,mid+1,right);
        }
    }

    public static <T extends Comparable<? super T>> void merge(T[] arr,T[] tempArr,int leftPos,int rightPos,int rightEnd){
        int leftEnd = rightPos-1;
        int tempPos = leftPos;
        int num = rightEnd-leftPos+1;

        while (leftPos<=leftEnd & rightPos<=rightEnd){
            if (arr[leftPos].compareTo(arr[rightPos])<0){
                tempArr[tempPos++] = arr[leftPos++];
            }else {
                tempArr[tempPos++] = arr[rightPos++];
            }
        }
        while (leftPos<=leftEnd){
            tempArr[tempPos++] = arr[leftPos++];
        }
        while (rightPos<=rightEnd){
            tempArr[tempPos++] = arr[rightPos++];
        }

        for (int i=0;i<num;i++,rightEnd--){
            arr[rightEnd] = tempArr[rightEnd];
        }
    }

    /**
     * 快速排序
     */
    public static <T extends Comparable<? super T>> void quickSort(List<T> arr){
        if (arr.size()<=1){
            arr.stream().forEach(x->System.out.print(x+"  "));
        }

        List<T> smaller = new ArrayList<>();
        List<T> same = new ArrayList<>();
        List<T> bigger = new ArrayList<>();

        T key = arr.get(arr.size()/2);
        for (T item : arr){
            if(item.compareTo(key)<0){
                smaller.add(item);
            }else if (item.compareTo(key)>0){
                bigger.add(item);
            }else {
                same.add(item);
            }
        }

        quickSort(smaller);
        quickSort(bigger);

        arr.clear();
        arr.addAll(smaller);
        arr.addAll(same);
        arr.addAll(bigger);
    }

}

猜你喜欢

转载自blog.csdn.net/meng984611383/article/details/82252231