排序算法

一、稳定性

直接插入排序、冒泡排序、归并排序、基数排序是稳定的

希尔排序、快速排序、选择排序、堆排序是不稳定的

二、时间复杂度

快速排序=堆排序=二路归并排序O(nlog2n)<希尔排序(n(log2n)^2)<直接插入=二分法插入=二路插入=共享栈插入=冒泡排序=直接选择排序(O(N^2))

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性复杂性
直接插入排序O(n2)O(n2)O(n2)O(n2)O(n)O(n)O(1)O(1)稳定简单
希尔排序O(nlog2n)O(nlog2n)O(n2)O(n2)O(n)O(n)O(1)O(1)不稳定较复杂
直接选择排序O(n2)O(n2)O(n2)O(n2)O(n2)O(n2)O(1)O(1)不稳定简单
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(1)O(1)不稳定较复杂
冒泡排序O(n2)O(n2)O(n2)O(n2)O(n)O(n)O(1)O(1)稳定简单
快速排序O(nlog2n)O(nlog2n)O(n2)O(n2)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)不稳定较复杂
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(n)O(n)稳定较复杂
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O(d(n+r))O(d(n+r))O(d(n+r))O(n+r)O(n+r)稳定较复杂

三、交换排序(冒泡排序、快速排序)

1.冒泡排序

  • 基本思想:两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。
import java.util.Scanner;

public class bubbleSort {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		while(sc.hasNext()) {
			String str = sc.nextLine();
			String[] str1 = str.split(" ");
			int[] num = new int[str1.length];
			for(int i = 0; i < str1.length; i++) {
				num[i] = Integer.parseInt(str1[i]);
			}
			System.out.println(bubblesort(num).toString());
		}
	}
	
	public static StringBuffer bubblesort(int[] num) {
		if(num.length < 1) {
			return null;
		}
		int temp = 0;
		for(int i = 0; i < num.length-1; i++) {
			boolean flag = false;
			for(int j = num.length-1; j > i; j--) {
				if(num[j-1] > num[j]) {
					temp = num[j-1];
					num[j-1] = num[j];
					num[j] = temp;
					flag = true;
				}
			}
			if(flag == false) {
				break;
			}
		}
		StringBuffer sb = new StringBuffer();
		for(int i = 0; i < num.length; i++) {
			sb.append(num[i]);
		}
		return sb;
	}
}

2.快速排序

  • 基本思想:在 当前无序区R[1..H]中任取一个数据元素作为比较的"基准"(不妨记为X)一般选第一个或最后一个,用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和 R[I+1..H],且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准X则位于最终排序的位置上,即 R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当R[1..I-1]和R[I+1..H]均非空时,分别对它们进行上述的划分过 程,直至所有无序子区中的数据元素均已排序为止。
public class QuickSort {
	
	 public static void quickSort(int[] arr,int low,int high){
	        int i,j,temp,t;
	        if(low>high){
	            return;
	        }
	        i=low;
	        j=high;
	        //temp就是基准位
	        temp = arr[low];

	        while (i<j) {
	            //先看右边,依次往左递减。判断temp是否比右边的数小,是右边的数就不用移动
	            while (temp<=arr[j]&&i<j) {
	                j--;
	            }
	            //再看左边,依次往右递增。判断左边的数是不是小于temp,是就不用移动
	            while (temp>=arr[i]&&i<j) {
	                i++;
	            }
	            //如果满足条件则交换
	            if (i<j) {
	                t = arr[j];
	                arr[j] = arr[i];
	                arr[i] = t;
	            }

	        }
	        //最后将基准为与i和j相等位置的数字交换
	         arr[low] = arr[i];
	         arr[i] = temp;
	        //递归调用左半数组
	        quickSort(arr, low, j-1);
	        //递归调用右半数组
	        quickSort(arr, j+1, high);
	    }


	    public static void main(String[] args){
	        int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
	        quickSort(arr, 0, arr.length-1);
	        for (int i = 0; i < arr.length; i++) {
	            System.out.println(arr[i]);
	        }
	    }

}

四、选择排序(简单选择排序、堆排序、树形选择排序)

1.简单选择排序

  • 基本思想:首先找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素,那么它就和自己交换。如果只剩下最后一个元素,就没必要排了,它就是最大的)。再次在剩下的元素中找最小的元素,将它与数组中的第二个元素交换。如此往复,直到将整个数组排序。
import java.util.Arrays;
public class SelectSort {
	
	public static void main(String[] args) {
		int[] a = {2,3,1,4,6,5,7};
		selectSort(a);
		System.out.print(Arrays.toString(a));
	}
	
	public static void selectSort(int[] a) {
		if(a == null || a.length < 1) {
			return;
		}
		for(int i = 0; i < a.length; i++) {
			int temp = a[i];
			int flag = i;//将当前下标定义为最小下标值
			for(int j = i + 1; j < a.length; j++) {
				if(a[j] < temp) {
					temp = a[j];
					flag = j;
				}
			}
			if(flag != i) {
				a[flag] = a[i];
				a[i] = temp;
			}
		}
	}
}

2.堆排序

  • 基本思想:堆排序正是利用小根堆(降序)(或大根堆(升序))来选取当前无序区中关键字小(或最大)的记录实现排序的。我们不妨利用大根堆来排序。每一趟排序的基本操作是:将当前无 序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和无序区中的最后一个记录交换。这样,正好和直接选择排序相反,有序区是在原记录区的尾部形成并逐 步向前扩大到整个记录区。
public class HeadSorting {
	   public static void main(String []args){
	        int []arr = {8,9,5,7,6,2,1,3,4};
	        sort(arr);
	        System.out.println(Arrays.toString(arr));
	    }
	    public static void sort(int []arr){
	        //1.构建大顶堆
	        for(int i=arr.length/2-1;i>=0;i--){
	            //从第一个非叶子结点从下至上,从右至左调整结构
	            adjustHeap(arr,i,arr.length);
	        }
	        //2.调整堆结构+交换堆顶元素与末尾元素
	        for(int j=arr.length-1;j>0;j--){
	            swap(arr,0,j);//将堆顶元素与末尾元素进行交换
	            adjustHeap(arr,0,j);//重新对堆进行调整
	        }

	    }

	    /**
	     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
	     * @param arr
	     * @param i
	     * @param length
	     */
	    public static void adjustHeap(int []arr,int i,int length){
	        int temp = arr[i];//先取出当前元素i
	        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
	            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
	                k++;
	            }
	            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
	                arr[i] = arr[k];
	                i = k;
	            }else{
	                break;
	            }
	        }
	        arr[i] = temp;//将temp值放到最终的位置
	    }

	    /**
	     * 交换元素
	     * @param arr
	     * @param a
	     * @param b
	     */
	    public static void swap(int []arr,int a ,int b){
	        int temp=arr[a];
	        arr[a] = arr[b];
	        arr[b] = temp;
	    }
	}

3.树形选择排序

  • 基本思路:树形选择排序也叫锦标赛排序,我们可以类比比赛过程。有n个待排序的元素,把它们两两一组进行比较,取出较小的,然后在这n/2个较小者中再两两一组进行比较,取出较小的,重复上述步骤,直到取出最小元素。 
       这个过程用一棵满二叉树表示,在选出最小元素后,将这个元素对应的叶子节点的值置为∞,然后把不为∞的兄弟节点移到父节点的位置。一直重复这个过程就可以了
public class TreeSelectSort {
	
	/* 对a[]按升序排列 */
    public static void treeSelectSort(int[] a) {
        int len = a.length;// 数组长度
        int nodeSize = len * 2 - 1; // 对一个满二叉树,节点总数 = 叶子节点数*2-1
        int[] tree = new int[nodeSize + 1]; // 这里将用数组表示二叉树的存储结构
        /* 填充叶子节点 */
        for (int i = len - 1, j = 0; i >= 0; i--, j++) {
            tree[nodeSize - j] = a[i];
        }
        /* 填充其他节点 */
        for (int i = nodeSize - len; i > 0; i--) {
            tree[i] = tree[i * 2] < tree[i * 2 + 1] ? tree[i * 2] : tree[i * 2 + 1];
        }
        /* 将每次找出的最小元素移走 */
        int index = 0;// 数组a的索引
        int minIndex = 0;// 最小值的索引
        while (index < len) {
            int min = tree[1]; // 这是tree的根节点,也是最小元素
            a[index++] = tree[1]; // 将tree中最小的元素取到a[0]中
            minIndex = nodeSize;
            /* 从最后的叶子节点开始,直到找到最小值的索引 */
            while (tree[minIndex] != min) {
                minIndex--;
            }
            tree[minIndex] = Integer.MAX_VALUE; // 将这个最小元素置为最大
            /* 如果这个节点还有父节点,那么就将它的兄弟节点升到父亲节点位置 */
            while (minIndex > 1) {// 根结点的索引是1
                if (minIndex % 2 == 0) {// 这个节点是左节点
                    tree[minIndex / 2] = tree[minIndex] < tree[minIndex + 1] ? tree[minIndex] : tree[minIndex + 1];
                    minIndex = minIndex / 2;
                } else {// 这个节点是右节点
                    tree[minIndex / 2] = tree[minIndex] < tree[minIndex - 1] ? tree[minIndex] : tree[minIndex - 1];
                    minIndex = minIndex / 2;
                }
            }
        }

    }/* treeSelectSort */

    public static void main(String[] args) {
        int[] a = { 12, 34, 23, 38, 65, 97, 76, 13 };
        TreeSelectSort.treeSelectSort(a);
        for (int i : a) {
            System.out.print(i + "  ");
        }
    }
}

五、插入排序(直接插入排序、希尔排序)

1.直接插入排序

  • 基本思路:每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
import java.util.Arrays;

public class InsertSort {
	public static void main(String[] args) {
		int[] a = {2,4,3,1,6,5,8};
		sort(a);
		System.out.print(Arrays.toString(a));
	}
	
	public static void sort(int[] a) {
		for(int i = 1; i < a.length; i++) {
			int temp = a[i]; //待比较的数
			int j = i;
			while(j > 0 && temp < a[j-1]) {
				a[j] = a[j-1];
				j--;
			}
			if(i != j) {
				a[j] = temp;
			}
		}
	}

}

2.希尔排序

  • 基本思路:先 取一个小于n的整数d1(d/2)作为第一个增量,把文件的全部记录分成d1组。所有距离为d1的倍数的记录放在同一组中。先在各组内进行直接插入排序,然后取第二 个增量d2<d1重复上述的分组和排序,直到所取的增量dt=1,即所有记录放在同一组中进行直接插入排序为止。该方法实际上是一种分组插入方法。
public class ShellSort {
	public static void main(String[] args) {		
		int[] arr = {2,3,1,4,6,8,5,7};		
		System.out.println("希尔排序前:"+Arrays.toString(arr));		
		shellSort(arr);		
		System.out.println("希尔排序后:"+Arrays.toString(arr));	
		}		
	/**	 
	 * 
	 * *希尔排序	 
	 *  @param arr 待排数组	 
	 */	
	public static void shellSort(int[] arr) {		
		for(int gap=arr.length/2; gap>0; gap/=2) { /*步长逐渐减小*/			
			for(int i=gap; i<arr.length; i++) { /*在同一步长内*/				
				//同一步长内排序方式是插入排序				
				int temp = arr[i], j; //待排元素				
				//j-gap代表有序数组中最大数的下标,j-pag表示有序数组的前一个元素,减pag是减去偏移量就是步长				
				for(j=i; j>=gap && temp<arr[j-gap]; j-=gap)					
					arr[j] = arr[j-gap]; //原有序数组最大的后移一位				
				arr[j] = temp; //找到了合适的位置插入			
				}		
			}	
		}
	}

六、归并排序

  • 基本思路:

       1. 从下往上的归并排序:将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有           序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就           得到了我们想要的排序结果。

       2. 从上往下的归并排序:它与"从下往上"在排序上是反方向的。它基本包括3步:
          ① 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
          ② 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。
          ③ 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]

/*
 * 将一个数组中的两个相邻有序区间合并成一个
 *
 * 参数说明:
 *     a -- 包含两个有序区间的数组
 *     start -- 第1个有序区间的起始地址。
 *     mid   -- 第1个有序区间的结束地址。也是第2个有序区间的起始地址。
 *     end   -- 第2个有序区间的结束地址。
 */
void merge(int a[], int start, int mid, int end)
{
    int *tmp = (int *)malloc((end-start+1)*sizeof(int));    // tmp是汇总2个有序区的临时区域
    int i = start;            // 第1个有序区的索引
    int j = mid + 1;        // 第2个有序区的索引
    int k = 0;                // 临时区域的索引

    while(i <= mid && j <= end)
    {
        if (a[i] <= a[j])
            tmp[k++] = a[i++];
        else
            tmp[k++] = a[j++];
    }

    while(i <= mid)
        tmp[k++] = a[i++];

    while(j <= end)
        tmp[k++] = a[j++];

    // 将排序后的元素,全部都整合到数组a中。
    for (i = 0; i < k; i++)
        a[start + i] = tmp[i];

    free(tmp);
}
import java.util.Arrays;

public class MergeSort {
	
	public static void main(String[] args) {
		int[] a = {3,4,2,1,6,5};
		int high = a.length-1;
		sort(a, 0, high);
		System.out.print(Arrays.toString(a));
	}
	
	public static int[] sort(int[] a,int low,int high){
        int mid = (low+high)/2;
        if(low<high){
            sort(a,low,mid);
            sort(a,mid+1,high);
            //左右归并
            merge(a,low,mid,high);
        }
        return a;
    }
     
    public static void merge(int[] a, int low, int mid, int high) {
        int[] temp = new int[high-low+1];
        int i= low;
        int j = mid+1;
        int k=0;
        // 把较小的数先移到新数组中
        while(i<=mid && j<=high){
            if(a[i]<a[j]){
                temp[k++] = a[i++];
            }else{
                temp[k++] = a[j++];
            }
        }
        // 把左边剩余的数移入数组 
        while(i<=mid){
            temp[k++] = a[i++];
        }
        // 把右边边剩余的数移入数组
        while(j<=high){
            temp[k++] = a[j++];
        }
        // 把新数组中的数覆盖nums数组
        for(int x=0;x<temp.length;x++){
            a[x+low] = temp[x];
        }
    }

}

七、基数排序

基本思路:将整数按位数切割成不同的数字,然后按每个位数分别比较。将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

public class BaseSort {
	 /*
	      * 获取数组a中最大值
	      *
	      * 参数说明:
	      *     a -- 数组
	      *     n -- 数组长度
	      */
	     private static int getMax(int[] a) {
	         int max;
	         max = a[0];
	         for (int i = 1; i < a.length; i++)
	             if (a[i] > max)
	                 max = a[i];
	         return max;
	     }
	 
	     /*
	      * 对数组按照"某个位数"进行排序(桶排序)
	      *
	      * 参数说明:
	      *     a -- 数组
	      *     exp -- 指数。对数组a按照该指数进行排序。
	      *
	      * 例如,对于数组a={50, 3, 542, 745, 2014, 154, 63, 616};
	      *    (01) 当exp=1表示按照"个位"对数组a进行排序
	      *    (02) 当exp=10表示按照"十位"对数组a进行排序
	      *    (03) 当exp=100表示按照"百位"对数组a进行排序
	      *    ...
	      */
	     private static void countSort(int[] a, int exp) {
	         //int output[a.length];    // 存储"被排序数据"的临时数组
	         int[] output = new int[a.length];    // 存储"被排序数据"的临时数组
	         int[] buckets = new int[10];
	 
	         // 将数据出现的次数存储在buckets[]中
	         for (int i = 0; i < a.length; i++)
	             buckets[ (a[i]/exp)%10 ]++;
	
	         // 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
	         for (int i = 1; i < 10; i++)
	             buckets[i] += buckets[i - 1];
	 
	        // 将数据存储到临时数组output[]中
	         for (int i = a.length - 1; i >= 0; i--) {
	             output[buckets[ (a[i]/exp)%10 ] - 1] = a[i];
	             buckets[ (a[i]/exp)%10 ]--;
	         }
	 
	         // 将排序好的数据赋值给a[]
	         for (int i = 0; i < a.length; i++)
	             a[i] = output[i];
	 
	         output = null;
	         buckets = null;
	    }
	 
	     /*
	      * 基数排序
	      *
	      * 参数说明:
	      *     a -- 数组
	      */
	     public static void radixSort(int[] a) {
	         int exp;    // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;...
	         int max = getMax(a);    // 数组a中的最大值
	 
	         // 从个位开始,对数组a按"指数"进行排序
	         for (exp = 1; max/exp > 0; exp *= 10)
	             countSort(a, exp);
	     }
	 
	     public static void main(String[] args) {
	         int i;
	         int a[] = {53, 3, 542, 748, 14, 214, 154, 63, 616};
	 
	         System.out.printf("before sort:");
	         for (i=0; i<a.length; i++)
	             System.out.printf("%d ", a[i]);
	         System.out.printf("\n");
	 
	         radixSort(a);    // 基数排序
	 
	         System.out.printf("after  sort:");
	         for (i=0; i<a.length; i++)
	             System.out.printf("%d ", a[i]);
	         System.out.printf("\n");
	     }

}

八、二分查找排序

  • 基本思路:

    将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;

    如果x>a[n/2],则继续在数组a的左半边查找x,如果x<a[n/2],则在数组a的右半边查找x.

  • 要求:(1)必须采用顺序存储结构 (2).必须按关键字大小有序排列
public class dichotomy {
	public static void main(String[] args) {
		int[] a = {1,2,3,4,5,6,7,8};
		int num = 2;
		System.out.println(recursiveBinarySearch(a,num,0,a.length-1));
		System.out.println(loopBinarySearch(a,num,0,a.length-1));
	}
	
	//递归方法
	public static int recursiveBinarySearch(int[] a, int num, int begin, int end) {
		if(num < a[0] || num > a[a.length-1] || begin > end) {
			return -1;
		}
		int mid = (begin + end)/2;
		if(num == a[mid]) {
			return mid;
		}else if(num < a[mid]) {
			return recursiveBinarySearch(a,num,0,mid-1);
		}else {
			return recursiveBinarySearch(a,num,mid+1,end);
		}
	}
	
	//循环二分法
	public static int loopBinarySearch(int[] a, int num, int begin, int end) {
		int beginIndex = begin;
		int endIndex = end;
		while(beginIndex < endIndex && (num < a[endIndex] && num > a[beginIndex])) {
			int mid = (beginIndex+endIndex)/2;
			if(num == a[mid]) {
				return mid;
			}else if(num > a[mid]) {
				beginIndex = mid + 1;
			}else {
				endIndex = mid -1;
			}
		}
		return -1;
	} 

}

九、二叉树的遍历

  • 基本思路:前序遍历:根左右,中序遍历:左根右,后序遍历:左右根

1.先创建结点类

public class Node {
	
    private int data;  
    private Node leftNode;  
    private Node rightNode;  
    public Node(int data, Node leftNode, Node rightNode){  
        this.data = data;  
        this.leftNode = leftNode;  
        this.rightNode = rightNode;  
    }  
  
    public int getData() {  
        return data;  
    }  
    public void setData(int data) {  
        this.data = data;  
    }  
    public Node getLeftNode() {  
        return leftNode;  
    }  
    public void setLeftNode(Node leftNode) {  
        this.leftNode = leftNode;  
    }  
    public Node getRightNode() {  
        return rightNode;  
    }  
    public void setRightNode(Node rightNode) {  
        this.rightNode = rightNode;  
    }  

}

2.利用递归方法遍历二叉树

public class BinaryTree {
	  
    public Node init() {//注意必须逆序建立,先建立子节点,再逆序往上建立,因为非叶子结点会使用到下面的节点,而初始化是按顺序初始化的,不逆序建立会报错  
        Node J = new Node(8, null, null);  
        Node H = new Node(4, null, null);  
        Node G = new Node(2, null, null);  
        Node F = new Node(7, null, J);  
        Node E = new Node(5, H, null);  
        Node D = new Node(1, null, G);  
        Node C = new Node(9, F, null);  
        Node B = new Node(3, D, E);  
        Node A = new Node(6, B, C);  
        return A;   //返回根节点  
    }
    
    public void printNode(Node node){  
        System.out.print(node.getData());  
    }  
    public void theFirstTraversal(Node root) {  //先序遍历  
        printNode(root);  
        if (root.getLeftNode() != null) {  //使用递归进行遍历左孩子  
            theFirstTraversal(root.getLeftNode());  
        }  
        if (root.getRightNode() != null) {  //递归遍历右孩子  
            theFirstTraversal(root.getRightNode());  
        }  
    }  
    public void theInOrderTraversal(Node root) {  //中序遍历  
        if (root.getLeftNode() != null) {  
            theInOrderTraversal(root.getLeftNode());  
        }  
        printNode(root);  
        if (root.getRightNode() != null) {  
            theInOrderTraversal(root.getRightNode());  
        }  
    }
    
    
    public void thePostOrderTraversal(Node root) {  //后序遍历  
        if (root.getLeftNode() != null) {  
            thePostOrderTraversal(root.getLeftNode());  
        }  
        if(root.getRightNode() != null) {  
            thePostOrderTraversal(root.getRightNode());  
        }  
        printNode(root);  
    }  
      
    public static void main(String[] args) {  
        BinaryTree tree = new BinaryTree();  
        Node root = tree.init();  
        System.out.println("先序遍历");  
        tree.theFirstTraversal(root);  
        System.out.println("");  
        System.out.println("中序遍历");  
        tree.theInOrderTraversal(root);  
        System.out.println("");  
        System.out.println("后序遍历");  
        tree.thePostOrderTraversal(root);  
        System.out.println("");  
    }  


}

2.利用堆栈方式实现

import java.util.Stack;

public class BinaryTree1 {
	public Node init() {//注意必须逆序建立,先建立子节点,再逆序往上建立,因为非叶子结点会使用到下面的节点,而初始化是按顺序初始化的,不逆序建立会报错  
        Node J = new Node(8, null, null);  
        Node H = new Node(4, null, null);  
        Node G = new Node(2, null, null);  
        Node F = new Node(7, null, J);  
        Node E = new Node(5, H, null);  
        Node D = new Node(1, null, G);  
        Node C = new Node(9, F, null);  
        Node B = new Node(3, D, E);  
        Node A = new Node(6, B, C);  
        return A;   //返回根节点  
    } 

public void printNode(Node node){  
    System.out.print(node.getData());  
}


public void theFirstTraversal_Stack(Node root) {  //先序遍历  
    Stack<Node> stack = new Stack<Node>();  
    Node node = root;  
    while (node != null || stack.size() > 0) {  //将所有左孩子压栈  
        if (node != null) {   //压栈之前先访问  
            printNode(node);  
            stack.push(node);  
            node = node.getLeftNode();  
        } else {  
            node = stack.pop();  
            node = node.getRightNode();  
        }  
    }  
}  
  
public void theInOrderTraversal_Stack(Node root) {  //中序遍历  
    Stack<Node> stack = new Stack<Node>();  
    Node node = root;  
    while (node != null || stack.size() > 0) {  
        if (node != null) {  
            stack.push(node);   //直接压栈  
            node = node.getLeftNode();  
        } else {  
            node = stack.pop(); //出栈并访问  
            printNode(node);  
            node = node.getRightNode(); 
        }  
    }  
}  
  
public void thePostOrderTraversal_Stack(Node root) {   //后序遍历  
    Stack<Node> stack = new Stack<Node>();  
    Stack<Node> output = new Stack<Node>();//构造一个中间栈来存储逆后序遍历的结果  
    Node node = root;  
    while (node != null || stack.size() > 0) {  
        if (node != null) {  
            output.push(node);  
            stack.push(node);                 
            node = node.getRightNode();  
        } else {  
            node = stack.pop();               
            node = node.getLeftNode();
        }  
    }  
    //System.out.println(output.size());
    while (output.size() > 0) {
        
        printNode(output.pop());  
    }  
}

public static void main(String[] args) {  
    BinaryTree1 tree = new BinaryTree1();  
    Node root = tree.init();  
    System.out.println("先序遍历");  
    tree.theFirstTraversal_Stack(root);  
    System.out.println("");  
    System.out.println("中序遍历");  
    tree.theInOrderTraversal_Stack(root);  
    System.out.println("");  
    System.out.println("后序遍历");  
    tree.thePostOrderTraversal_Stack(root);  
    System.out.println("");  
}

}

十、深度优先遍历和广度优先遍历

1.深度优先遍历:根据深度优先遍历的特点我们利用Java集合类的栈Stack先进后出的特点来实现

  1. 首先节点 1 进栈,节点1在栈顶;
  2. 然后节点1出栈,访问节点1,节点1的孩子节点3进栈,节点2进栈;
  3. 节点2在栈顶,然后节点2出栈,访问节点2
  4. 节点2的孩子节点5进栈,节点4进栈
  5. 节点4在栈顶,节点4出栈,访问节点4,
  6. 节点4左右孩子为空,然后节点5在栈顶,节点5出栈,访问节点5;
  7. 节点5左右孩子为空,然后节点3在站顶,节点3出栈,访问节点3;
  8. 节点3的孩子节点7进栈,节点6进栈
  9. 节点6在栈顶,节点6出栈,访问节点6;
  10. 节点6的孩子为空,这个时候节点7在栈顶,节点7出栈,访问节点7
  11. 节点7的左右孩子为空,此时栈为空,遍历结束。

2.广度优先遍历:根据广度优先遍历的特点我们利用Java数据结构队列Queue来实现

  1. 节点1进队,节点1出队,访问节点1
  2. 节点1的孩子节点2进队,节点3进队。
  3. 节点2出队,访问节点2,节点2的孩子节点4进队,节点5进队;
  4. 节点3出队,访问节点3,节点3的孩子节点6进队,节点7进队;
  5. 节点4出队,访问节点4,节点4没有孩子节点。
  6. 节点5出队,访问节点5,节点5没有孩子节点。
  7. 节点6出队,访问节点6,节点6没有孩子节点。
  8. 节点7出队,访问节点7,节点7没有孩子节点,结束。

3.构造二叉树结构

public class TreeNode {
	
	int data;	TreeNode leftNode;	
	TreeNode rightNode;	
	public TreeNode() {			
		
	}	
	public TreeNode(int d) {		
		data=d;	
		}		
	public TreeNode(TreeNode left,TreeNode right,int d) {		
		leftNode=left;		
		rightNode=right;		
		data=d;	
		}
}

4.代码实现

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class DeepFirstSort {
	public static void main(String[] args) {		
		TreeNode head=new TreeNode(1);		
		TreeNode second=new TreeNode(2);		
		TreeNode three=new TreeNode(3);		
		TreeNode four=new TreeNode(4);		
		TreeNode five=new TreeNode(5);		
		TreeNode six=new TreeNode(6);		
		TreeNode seven=new TreeNode(7);		
		head.rightNode=three;		
		head.leftNode=second;		
		second.rightNode=five;		
		second.leftNode=four;		
		three.rightNode=seven;		
		three.leftNode=six;		
		System.out.print("广度优先遍历结果:");		
		new DeepFirstSort().BroadFirstSearch(head);		
		System.out.println();		
		System.out.print("深度优先遍历结果:");		
		new DeepFirstSort().depthFirstSearch(head);	}		
	
	//广度优先遍历是使用队列实现的	
	public void BroadFirstSearch(TreeNode nodeHead) {		
		if(nodeHead==null) {			
			return;		
			}		
		Queue<TreeNode> myQueue=new LinkedList<>();		
		myQueue.add(nodeHead);		
		while(!myQueue.isEmpty()) {			
			TreeNode node=myQueue.poll();			
			System.out.print(node.data+" ");			
			if(null!=node.leftNode) {				
				myQueue.add(node.leftNode);    
				//广度优先遍历,我们在这里采用每一行从左到右遍历			
				}			
			if(null!=node.rightNode) {				
				myQueue.add(node.rightNode);			
				}					
			}	
		}
	
	//深度优先遍历	
	public void depthFirstSearch(TreeNode nodeHead) {		
		if(nodeHead==null) {			
			return;		
			}		
		Stack<TreeNode> myStack=new Stack<>();		
		myStack.add(nodeHead);		
		while(!myStack.isEmpty()) {			
			TreeNode node=myStack.pop();    
			//弹出栈顶元素			
			System.out.print(node.data+" ");			
			if(node.rightNode!=null) {				
				myStack.push(node.rightNode);    
				//深度优先遍历,先遍历左边,后遍历右边,栈先进后出			
				}			
			if(node.leftNode!=null) {				
				myStack.push(node.leftNode);			
				}		
			}			
		}
	}

5.队列

add         增加一个元索                          如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
element  返回队列头部的元素               如果队列为空,则抛出一个NoSuchElementException异常
offer        添加一个元素并返回true         如果队列已满,则返回false
poll         移除并返问队列头部的元素     如果队列为空,则返回null
peek       返回队列头部的元素                如果队列为空,则返回null
put          添加一个元素                           如果队列满,则阻塞
take        移除并返回队列头部的元素     如果队列为空,则阻塞

6.广度优先就相当于,先将头节点入队列,然后头节点出队列保存在node中,再将头节点对应的左右孩子依次入队列,在第二次循环中先将左孩子出队列,右孩子不动。再将左孩子的左右孩子入队列。第三次循环,将右孩子出队列,右孩子的左右孩子依次出队列。依次循环,一次只出一个元素,并将出队列相对应的左右孩子入队列。

7.栈

   1-->public Stack()创建一个空堆栈

   2-->public boolean empty()测试堆栈是否为空;

   3-->public E pop()移除堆栈顶部的对象,并作为此函数的值返回该对象。 

   4-->public E push(E item)把项压入堆栈顶部

   5-->public E peek()查看堆栈顶部的对象,但不从堆栈中移除它。 

   6-->public boolean empty()测试堆栈是否为空

8.深度优先遍历相当于先将根节点入栈再出栈,然后将根节点对应的右孩子、左孩子依次入栈。第二次循环,将栈顶的左孩子出栈,再将左孩子对应的右孩子和左孩子依次入栈。第三次循环将栈顶的左孩子出栈,该左孩子的左右子树为空,则不进行入栈操作。第四次循环将栈顶的右孩子出栈。依次循环。

十一、利用栈实现字符串反转

import java.util.Stack;

public class StringReverse {
	public static void main(String[] args) {
		String[] a = {"a","s","f","d","g","h","j","k"};
		Stack<String> st = new Stack<>();
		for(int i = 0; i < a.length; i++) {
			st.push(a[i]);
		}
		while(!st.isEmpty()){
			System.out.print(st.pop());
		}
	}

}

十二、N进制转为M进制

先将N进制转为10进制(按位乘进制数),再将10进制转为M进制(求余再反转)

public class NToM {
	/*public static void main(String[] args) {
		String num = "1010";
		int N = 2;
		int M = 16;
		int a = Integer.parseInt(num,N);//十进制
		String b = Integer.toHexString(a);//十六进制
		String c = Integer.toOctalString(a);//八进制
		System.out.println(b);
		System.out.println(c);
	}*/
	
	public static void main(String[] args) {
		String origin ="101010";
		int N = 2;
		int M = 16;
		long ten = NToTen(origin, N);
		System.out.println(TenToAny(ten,M));
	}
	
	//N进制转10进制
	private static int NToTen(String origin, int N) {
		String[] elements = origin.split("");
		int mm = 0;
		for(int i = 0; i < elements.length; i++) {
			Character c = elements[i].charAt(0);
			mm += getByteNum(c)*Math.pow(N, elements.length-1-i);
		}
		System.out.println(mm);
		return mm;
	}
	
	//将字符转化成数字
	private static int getByteNum(Character c) {
		return Character.getNumericValue(c);
	}
	
	//10进制转为任意进制
	private static String TenToAny(long ten, int M) {
		long yu = 0;
		StringBuffer sb = new StringBuffer();
		String code = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		while(ten > 0) {
			yu = ten % M;
			ten = ten / M;
			sb.append(code.charAt((int) yu));
		}
		sb.reverse();
		return sb.toString();
	}

}

十三、归并扩展

1.两个有序数组合并成一个

public class TwoArrayToOne {
	public static void main(String[] args) {
		int[] a = {1,3,5,7};
		int[] b = {2,4,6,8};
		int[] result = twoArrayToOne(a,b);
		for(int i : result) {
			System.out.print(i);
		}
		
	}
	public static int[] twoArrayToOne(int[] a, int[] b) {
		if(a == null && b == null) {
			return null;
		}
		if(a == null) {
			return b;
		}
		if(b == null) {
			return a;
		}
		int la = a.length;
		int lb = b.length;
		int starta = 0;
		int startb = 0;
		int result = 0;
		int[] rs = new int[la+lb];
		while(starta < la && startb < lb) {
			if(a[starta] < b[startb]) {
				rs[result++] = a[starta++];
			}else {
				rs[result++] = b[startb++];
			}
		}
		while(starta < la) {
			rs[result++] = a[starta++];
		}
		while(startb < lb) {
			rs[result++] = b[startb++];
		}
		return rs;
	}

}

2.两个有序数组合并并去重

import java.util.TreeSet;

public class TwoArrayToOne1 {
	public static void main(String[] args) {
		int[] a = {1,3,5,7,9};
		int[] b = {2,4,6,7,8,9};
		int[] result = sort(a,b);
		for(int i : result) {
			System.out.print(i);
		}
	}
	public static int[] sort(int[] a, int[] b) {
		if(a == null && b == null) {
			return null;
		}
		if(a == null) {
			return b;
		}
		if(b == null) {
			return a;
		}
		TreeSet<Integer> tree = new TreeSet<>();
		int sa = 0;
		int sb = 0;
		while(sa < a.length) {
			tree.add(a[sa++]);
		}
		while(sb < b.length) {
			tree.add(b[sb++]);
		}
		Object[] obj = tree.toArray();
		int[] result = new int[obj.length];
		for(int i = 0; i<obj.length; i++) {
			result[i] = (int) obj[i];
		}
		return result;
	}

}

相关知识点:

1.利用TreeSet的唯一和有序,将数组全部存入tree set中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值