数据结构--排序-查询-二叉树各种遍历-求深度
来源:互联网 发布:淘宝羊绒哈伦九分女裤 编辑:程序博客网 时间:2024/06/14 04:37
排序:
排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面讲的排序都是属于内排序。
内排序有可以分为以下几类:
(1)、插入排序:直接插入排序、二分法插入排序、希尔排序。
(2)、选择排序:简单选择排序、堆排序。
(3)、交换排序:冒泡排序、快速排序。
(4)、归并排序
(5)、基数排序
一:关于各种排序稳定性
冒泡排序、插入排序、归并排序和基数排序都是稳定的排序算法
参考:http://www.cnblogs.com/Braveliu/archive/2013/01/15/2861201.html
二:关于各种排序的时间复杂度 空间复杂度
一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。若一个算法为递归算法,其空间复杂度为递归所使用的堆栈空间的大小,它等于一次调用所分配的临时存储空间的大小乘以被调用的次数(即为递归调用的次数加1,这个1表示开始进行的一次非递归调用)。算法的空间复杂度一般也以数量级的形式给出。如当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为O(log2n);当一个算法的空间复杂度与n成线性比例关系时,可表示为O(n).若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。
如对于递归算法来说,一般都比较简短,算法本身所占用的存储空间较少,但运行时需要一个附加堆栈,从而占用较多的临时工作单元;若写成非递归算法,一般可能比较长,算法本身占用的存储空间较多,但运行时将可能需要较少的存储单元。
注意:图片中快排空间复杂度不正确 最差O(N) 最好O(log2N)
1快速排序 最坏情况:在枢纽元选为第一个元素,如果输入时随机的,那么是可以接受的,但是若输入时正序的或者反序,那么这个枢纽元就会产生一个劣质的分割,因为所有的元素不是被划入s1就是被划入s2 ,更糟糕的是这会发生在所有的递归调用,其实什么也没干,因此最坏时间是二次的。我们可以容易的通过枢纽元的选取打到较快的快速排序时间。
2快速排序每次将待排序数组分为两个部分,在理想状况下,每一次都将待排序数组划分成等长两个部分,则需要logn次划分(即递归次数)。 而在最坏情况下,即数组已经有序或大致有序的情况下,每次划分只能减少一个元素,快速排序将不幸退化为冒泡排序,所以快速排序时间复杂度下界为O(nlogn),最坏情况为O(n^2)。在实际应用中,快速排序的平均时间复杂度为O(nlogn)。快速排序在对序列的操作过程中只需花费常数级的空间。空间复杂度O(1)。 但需要注意递归栈上需要花费最少logn 最多n的空间
快速排序空间复杂度只是在通常情况下才为O(log2n),如果是最坏情况的话,很显然就要O(n)的空间了。当然,可以通过随机化选择pivot来将空间复杂度降低到O(log2n)
3 归并排序 是递归的 需要log2N次 但是至始至终只需要一个N的相同大小数组即可 因此空间复杂度O(N)+O(log2N)=O(N)
三:总结
1、稳定
稳定:冒泡排序、插入排序、归并排序和基数排序
不稳定:选择排序、快速排序、希尔排序、堆排序
2、平均时间复杂度
O(n^2):直接插入排序,简单选择排序,冒泡排序。
在数据规模较小时(9W内),直接插入排序,简单选择排序差不多。当数据较大时,冒泡排序算法的时间代价最高。性能为O(n^2)的算法基本上是相邻元素进行比较,基本上都是稳定的。
O(nlogn):快速排序,归并排序,希尔排序,堆排序
其中,快排是最好的, 其次是归并和希尔,堆排序在数据量很大时效果明显。
3、排序算法的选择
1.数据规模较小
(1)待排序列基本序的情况下,可以选择直接插入排序;
(2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
2.数据规模不是很大
(1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。
(2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
3.数据规模很大
(1)对稳定性有求,则可考虑归并排序。
(2)对稳定性没要求,宜用堆排序
4.序列初始基本有序(正序),宜用直接插入,冒泡
四:代码实现(Java)
public class 插入_直接插入排序{ /** * <插入排序--直接插入排序> * * 插入排序: * * 思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置,直到全部插入排序完为止 * * 关键问题:在前面已经排好序的序列中找到合适的插入位置 * * 直接插入排序:(从后向前找到合适位置后插入) */ public static void main(String[] args) { //排序之前 int[] arr={10,4,15,6,1,16,12,25,11,2}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============直接插入排序后==================="); for (int i = 1; i < arr.length; i++) { //将等待比较的元素拿出来 int temp=arr[i]; int j; for (j = i-1; j >=0; j--) { //大于temp的数都向后移动 注意加了=就不是稳定的了 if(arr[j]>temp) arr[j+1]=arr[j]; else break; } arr[j+1]=temp; } for (int a : arr) { System.out.print(a+" "); } /* * 分析 直接插入排序是稳定的排序。关于各种算法的稳定性分析可以参考http://www.cnblogs.com/Braveliu/archive/2013/01/15/2861201.html 文件初态不同时,直接插入排序所耗费的时间有很大差异。若文件初态为正序, 则每个待插入的记录只需要比较一次就能够找到合适的位置插入,故算法的时间复杂度为O(n), 这时最好的情况。若初态为反序,则第i个待插入记录需要比较i+1次才能找到合适位置插入, 故时间复杂度为O(n2),这时最坏的情况。 直接插入排序的平均时间复杂度为O(n2)。 */ } }
public class 插入_二分插入排序{ /** * <插入排序--二分插入排序> * * 插入排序: * * 思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置,直到全部插入排序完为止 * * 关键问题:在前面已经排好序的序列中找到合适的插入位置 * * 二分插入排序:(二分法插入排序的思想和直接插入一样,只是找合适的插入位置的方式不同,这里是按二分法找到合适的位置,可以减少比较的次数。) */ public static void main(String[] args) { //排序之前 int[] arr={10,4,15,6,1,16,12,25,11,2,16}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============二分插入排序后==================="); sort(arr); for (int a : arr) { System.out.print(a+" "); } } private static void sort(int[] arr) { for (int i = 0; i < arr.length; i++) { //每次都要在前面序列中用二分法找到合适的插入位置 int temp=arr[i]; int left=0; int right=i-1; int mid=0; while(left<=right) { mid=(left+right)/2; if(temp<arr[mid]) right=mid-1; else left=mid+1; } //此时已经找到插入位置left 整体移位 for (int j = i-1; j >=left; j--) { arr[j+1]=arr[j]; } if(left!=i)//若left等于i 说明序号i之前的序列都比i小 无需交换 arr[left]=temp; /* * 当然,二分法插入排序也是稳定的。 二分插入排序的比较次数与待排序记录的初始状态无关, 仅依赖于记录的个数。当n较大时,比直接插入排序的最大比较次数少得多。但大于直接插入排序的最小比较次数。 最好时间复杂度为O(n)最差时间复杂度为O(n2) 平均时间复杂度为O(n2) */ } } }
public class 插入_希尔排序{ /** * <插入排序--希尔排序> * * 插入排序: * * 思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置,直到全部插入排序完为止 * * 关键问题:在前面已经排好序的序列中找到合适的插入位置 * * 希尔排序:基本思想:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。 * 所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后, * 取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1), * 即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法 */ public static void main(String[] args) { //排序之前 int[] arr={10,4,15,6,1,16,12,25,11,2,16,7}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============希尔排序后==================="); sort(arr); for (int a : arr) { System.out.print(a+" "); } } private static void sort(int[] arr) { int d=arr.length/2; while(true) { d/=2; for (int x = 0; x < d; x++) { for (int i = x+d; i < arr.length; i=i+d) { int temp=arr[i]; int j; for (j = i-d; j>=0 && temp<arr[j]; j=j-d) { arr[j+d]=arr[j]; } arr[j+d]=temp; } } if(d==1) break; } /* * 分析: * 我们知道一次插入排序是稳定的,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动, * 最后其稳定性就会被打乱,所以希尔排序是不稳定的。 * 希尔排序的时间性能优于直接插入排序,原因如下: (1)当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。 (2)当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。 (3)在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快, 后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序, 使文件较接近于有序状态,所以新的一趟排序过程也较快。 因此,希尔排序在效率上较直接插人排序有较大的改进。 希尔排序的平均时间复杂度为O(nlogn)。没有最好 最坏 */ }}
public class 选择_简单选择排序{ /** * <选择排序--简单选择排序> * * 插入排序: * * 思想:每趟从待排序的记录序列中选择关键字最小的记录放置到已排序表的最前位置,直到全部排完。 * * 关键问题:在剩余的待排序记录序列中找到最小关键码记录 * * 直接插入排序:在要排序的一组数中,选出最小的一个数与第一个位置的数交换; * 然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止 */ public static void main(String[] args) { //排序之前 int[] arr={10,4,15,6,1,16,12,25,11,2,9}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============简答选择排序后==================="); for (int i = 0; i < arr.length; i++) { int min=arr[i];//每次暂时以剩余的第一个为最小 int n=i;//记录最小数的索引位置 for(int j=i+1;j<arr.length;j++) { if(arr[j]<min) { min=arr[j]; n=j; } } //剩余的第一个和最小索引处的值进行交换 arr[n]=arr[i]; arr[i]=min; } for (int a : arr) { System.out.print(a+" "); } /* * 分析 简单选择排序是不稳定的排序。 原始序列: 21,25,49,25*,16,08 排序后:08,16, 21,25*,25,49 两个25的位置变化了,所以是不稳定的 时间复杂度:T(n)=O(n2) */ } }
public class 选择_堆排序{ /** * <选择排序--堆排序> * * 堆排序是一种树形选择排序,是对直接选择排序的有效改进 * * 堆的定义下:具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1) * 或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出, * 堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树 * * 思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序, * 使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。 * 然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换, * 最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆, * 二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数, * 二是反复调用渗透函数实现排序的函数 * */ public static void main(String[] args) { //排序之前 堆序号和数组序号相同 int[] arr={10,4,15,6,1,16,12,25,11,2,9,13}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============堆排序后==================="); heapSort(arr); System.out.println(Arrays.toString(arr)); } //左儿子 堆序的下标从0开始 private static int leftChild(int i) { return i*2+1; } //交换 private static void swap(int[] arr,int i,int j) { int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } //perculateDown下沉操作 把该数字下方的尽可能小的地方 大的往上走一个 //arrLength数组长度 可变化 private static void perculateDown(int[] arr,int i,int arrLength) { int child; int temp;//把要比的这个值拿出来一个个比 比完了再把此值放到对应的i中 for(temp=arr[i];leftChild(i)<=arrLength-1;i=child) { child=leftChild(i); //如果有右孩子 只有一种情况可能没有右孩子 就是左孩子为最后一个孩子时 //若右孩子大 当前i就和右孩子比较 if(child!=arrLength-1 && arr[child]<arr[child+1]) child++; if(temp<arr[child]) arr[i]=arr[child]; else break; } arr[i]=temp; } //先建立堆 利用0-arr.length/2个可能有孩子的节点开始沉降就可以建立堆 //把最大值和最后一个位置交换 再对第一个位置沉降 如此循环 private static void heapSort(int[] arr) { //建立堆 for(int i=arr.length/2;i>=0;i--) { perculateDown(arr,i,arr.length); } //交换并删除最大 for(int i=arr.length-1;i>0;i--) { swap(arr,0,i); perculateDown(arr,0,i); } } /* * 堆排序也是一种不稳定的排序算法。 堆排序优于简单选择排序的原因: 直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较, 然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中, 有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果, 所以后一趟排序时又重复执行了这些比较操作。 堆排序可通过树形结构保存部分比较结果,可减少比较次数。 堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。 由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。 */ }
public class 交换_冒泡{ /** * <交换排序--冒泡排序> * * * 基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整, * 让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换 */ public static void main(String[] args) { //排序之前 int[] arr={10,4,15,6,1,16,12,25,11,2,9,3}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============冒泡排序后==================="); //要把n个数冒出来 没冒一次 都要比较交换许多次 已经冒出来的 不需要再进行比较 for(int i=0;i<arr.length;i++) { for(int j=0;j<arr.length-1-i;j++) { if(arr[j]>arr[j+1]) { int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } System.out.println(Arrays.toString(arr)); /* * 分析 冒泡排序是一种稳定的排序方法。 •若文件初状为正序,则一趟起泡就可完成排序,排序码的比较次数为n-1,且没有记录移动,时间复杂度是O(n) •若文件初态为逆序,则需要n-1趟起泡,每趟进行n-i次排序码的比较,且每次比较都移动三次,比较和移动次数均达到最大值∶O(n2) •起泡排序平均时间复杂度为O(n2) */ } }
public class 交换_快速排序{ /** * <交换排序--快速排序> * * * 基本思想:运用递归。 先找出枢纽元 分为两部分 一部分比枢纽元小 另一部分比它大 * 再进行递归 */ public static void main(String[] args) { //排序之前 int[] arr={11,2,9,23,7,5,11,10,8,3,13,4,1}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============快速排序后==================="); quickSort(arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); } //交换 private static void swap(int[] arr,int i,int j) { int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } //执行三数中值分割法的程序 //把left center right 先排序 center为枢纽元 再把center和right-1位置进行交换 private static int median3(int[] arr,int left,int right) { int center=(left+right)/2; if(arr[center]<arr[left]) swap(arr,center,left); if(arr[right]<arr[left]) swap(arr,right,left); if(arr[right]<arr[center]) swap(arr,right,center); //把枢纽元位置放在right-1处(程序最后再和i位置交换) j指针从right-2位置开始 swap(arr,center,right-1); return arr[right-1]; } private static void quickSort(int[] arr,int left,int right) { if(left<right) { int pivot=median3(arr,left,right); int dis=right-left; if(dis==1 || dis==2)//如果递归时 只剩两个或者三个元素 那么经过median3就已经排好序了 无需进行下面步骤 return; int i=left; int j=right-1; for(;;) { while(arr[++i]<pivot){} while(arr[--j]>pivot){} if(i<j) swap(arr,i,j); else break; } swap(arr,i,right-1); quickSort(arr,left,i-1);//排序更大元素 quickSort(arr,i+1,right);//排序更小元素 } } /* * 快速排序是不稳定的排序。 快速排序的时间复杂度为O(nlogn)。 当n较大时使用快排比较好,当序列基本有序时(若枢纽元取第一个的话)用快排反而不好 相当于每次只分了一个 */ }
public class 归并排序{ /** * <归并排序> * * * 基本思想:基本思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表, * 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列 */ public static void main(String[] args) { //排序之前 int[] arr={11,2,9,23,7,5,11,10,8,3,13,4,1}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============归并排序后==================="); mergeSort(arr); System.out.println(Arrays.toString(arr)); } //自始至终 arr和temp都只有一份 public static void mergeSort(int[] arr) { int[] temp=new int[arr.length]; mergeSort(arr,temp,0,arr.length-1); } private static void mergeSort(int[] arr, int[] temp, int left, int right) { if(left<right)//此处条件需要注意 { int center=(left+right)/2; mergeSort(arr,temp,left,center); mergeSort(arr,temp,center+1,right); merge(arr,temp,left,center+1,right); } } private static void merge(int[] arr, int[] temp, int leftPos, int rightPos, int rightEnd) { int leftEnd=rightPos-1; int tempPos=leftPos; int numElements=rightEnd-leftPos+1; //如果都没比完 while(leftPos<=leftEnd && rightPos<=rightEnd) { if(arr[leftPos]<=arr[rightPos]) temp[tempPos++]=arr[leftPos++]; else temp[tempPos++]=arr[rightPos++]; } //如果右边部分比完了 while(leftPos<=leftEnd) { temp[tempPos++]=arr[leftPos++]; } //如果左边部分比完了 while(rightPos<=rightEnd) { temp[tempPos++]=arr[rightPos++]; } //把temp copy给arr for(int i=0;i<numElements;i++,rightEnd--) { arr[rightEnd]=temp[rightEnd]; } } /* * 归并排序是稳定的排序方法。 归并排序的时间复杂度为O(nlogn)。 速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。 */}
public class 基数排序{ /** * 基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。 * 然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列 */ public static void main(String[] args) { //排序之前 int[] arr={11,2,9,23,7,20,5,17,11,10,8,20,3,13,4,1}; for (int a : arr) { System.out.print(a+" "); } System.out.println(); System.out.println("===============基数排序后==================="); sort(arr); System.out.println(Arrays.toString(arr)); } public static void sort(int[] arr) { //找到数组中最大值 用来确定位数几位 来确定循环次数 int max=0; for(int i=0;i<arr.length;i++) { if(arr[i]>max) max=arr[i]; } //确定位数 int times=0; while(max>0) { max=max/10; times++; } //建立10个队列 List<ArrayList> queue=new ArrayList<ArrayList>(); for(int i=0;i<10;i++) { ArrayList subQueue=new ArrayList(); queue.add(subQueue); } //进行times次分配和收集 for(int i=0;i<times;i++) { for(int j=0;j<arr.length;j++) { int x=arr[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i); ArrayList subQueue=queue.get(x); subQueue.add(arr[j]); // queue.set(x, subQueue);//注意这个可以不写 } //收集 int count=0; for(int k=0;k<10;k++) { while(queue.get(k).size()>0) { ArrayList<Integer> subQueue=queue.get(k); arr[count]=subQueue.get(0); count++; subQueue.remove(0);//删除之后会自动移位 } } } } /* * 快速排序是不稳定的排序。 基数排序是稳定的排序算法。 基数排序的时间复杂度为O(d(n+r)),d为位数,r为基数。 */ }
感谢:http://www.cnblogs.com/liuling/p/2013-7-24-01.html
查找:
一、顺序查找 条件:无序或有序队列。 原理:按顺序比较每个元素,直到找到关键字为止。 时间复杂度:O(n)二、二分查找(折半查找) 条件:有序数组 原理:查找过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束; 如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。 如果在某一步骤数组为空,则代表找不到。 这种搜索算法每一次比较都使搜索范围缩小一半。 时间复杂度:O(logn)三、哈希表(散列表) 条件:先创建哈希表(散列表) 原理:根据键值方式(Key value)进行查找,通过散列函数,定位数据元素。 时间复杂度:几乎是O(1),取决于产生冲突的多少。
public class 二分法查找_递归{ /** * <二分法是对有序数列进行的查找算法> * <这是递归方式的实现> * @param args * @see [类、类#方法、类#成员] */ public static void main(String[] args) { Integer[] a=new Integer[]{2,4,5,7,8,9,11}; int position=search(a,0,a.length-1,1); if(position!=-1) System.out.println("二分法查找后为:"+a[position]); else System.out.println("没找到"); } //返回的是具体数组位置的序号 public static <T extends Comparable<? super T>> int search(T[] a,int low,int high,T x) { if(low>high) return -1; int mid=(low+high)/2; if(a[mid].compareTo(x)>0) return search(a,low,mid-1,x); else if(a[mid].compareTo(x)<0) return search(a,mid+1,high,x); else return mid; } }
public class 二分法查找_非递归{ /** * <二分法是对有序数列进行的查找算法> * <这是非递归方式的实现> * @param args * @see [类、类#方法、类#成员] */ public static void main(String[] args) { Integer[] a=new Integer[]{2,4,5,7,8,9,11}; int position=search(a,0,a.length-1,7); if(position!=-1) System.out.println("二分法查找后为:"+a[position]); else System.out.println("没找到"); } //返回的是具体数组位置的序号 public static <T extends Comparable<? super T>> int search(T[] a,int low,int high,T x) { while(low<=high) { int mid=(low+high)/2; if(a[mid].compareTo(x)>0) high=mid-1; else if(a[mid].compareTo(x)<0) low=mid+1; else return mid; } return -1; } }
二叉树各种递归非递归遍历、求深度
public class Node{ private String element; public Node() { } public Node(String element) { this.element = element; } private Node left; private Node right; public Node(String element, Node left, Node right) { this.element = element; this.left = left; this.right = right; } public Node getLeft() { return left; } public void setLeft(Node left) { this.left = left; } public Node getRight() { return right; } public void setRight(Node right) { this.right = right; } /** * 重载方法 * @return */ @Override public String toString() { return element; }}
public class BinaryTree{ private Node root; public void initTree() { Node d = new Node("d"); Node e = new Node("e"); Node f = new Node("f"); Node g = new Node("g"); Node b = new Node("b", d, e); Node c = new Node("c", f, g); Node a = new Node("a", b, c); this.root = a; } public Node getRoot() { return root; } public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.initTree(); // preorder(tree.getRoot()); // inorder(tree.getRoot()); // postorder(tree.getRoot()); // nonPreorder(tree.getRoot()); // nonInorder(tree.getRoot()); // nonPostorder(tree.getRoot()); //cengxuOrder(tree.getRoot()); //quDongCengxuOrder(tree.getRoot()); //System.out.println(getMaxDepth(tree.getRoot())); System.out.println(nonGetMaxDepth(tree.getRoot())); } /* * 递归实现 前序遍历 */ public static void preorder(Node p) { if (p != null) { System.out.print(p + " "); preorder(p.getLeft()); preorder(p.getRight()); } } /* * 递归实现 前序遍历 */ public static void inorder(Node p) { if (p != null) { inorder(p.getLeft()); System.out.print(p + " "); inorder(p.getRight()); } } /* * 递归实现 后序遍历 */ public static void postorder(Node p) { if (p != null) { postorder(p.getLeft()); postorder(p.getRight()); System.out.print(p + " "); } } // ----------------------非递归实现 用Stack------------------------ /* * 非递归实现 前序遍历 */ public static void nonPreorder(Node p) { Stack<Node> stack = new Stack<Node>(); if (p != null) { stack.push(p);// 把根压入栈 while (!stack.empty()) { p = stack.pop(); // 前序 先打印当前节点 再把小弟们压入栈 // 如此往返 小弟先出来打印 第一个小弟出来后 再把它的小弟压进去 如此往返 System.out.print(p + " "); if (p.getRight() != null) stack.push(p.getRight());// 后压的在上面 if (p.getLeft() != null) stack.push(p.getLeft()); } } } /* * 非递归实现 中序遍历 每一个节点 先入站所有最左边的 然后自己 然后右边一个子节点 重要 每一个节点先全部 遍历左堆 再把自己打印 再全部遍历右堆 */ public static void nonInorder(Node p) { Stack<Node> stack = new Stack<Node>(); while (p != null)// 看右边第一个节点是否为空 是的话就结束 { while (p != null)// 主要看该节点左边是否为空 { if (p.getRight() != null) stack.push(p.getRight()); stack.push(p); p = p.getLeft(); } p = stack.pop(); while (!stack.empty() && p.getRight() == null)// 没有右字树 一路打印 { System.out.print(p + " "); p = stack.pop(); } System.out.print(p + " "); if (!stack.empty())// 若空 表示最顶层节点 没有右字树 结束 p = stack.pop(); else p = null; } } /* * 非递归实现 后序遍历 */ public static void nonPostorder(Node p) { Node q = p; Stack<Node> stack = new Stack<Node>(); while (p != null) { // 左子树入栈 for (; p.getLeft() != null; p = p.getLeft()) stack.push(p); // while (p != null && (p.getRight() == null || p.getRight() == q)) { System.out.print(p + " "); q = p;// 记录上个已经输出的节点 if (stack.empty()) return; p = stack.pop(); } // 处理右子 stack.push(p); p = p.getRight(); } } /* * 二叉树的层序遍历 非递归版本 用队列Queue(接口) Queue的父接口为Collection 和 Iterable 子接口包括了LinkedList 层序遍历二叉树,用队列实现,先将根节点入队列, * 只要队列不为空,然后出队列,并访问,接着讲访问节点的左右子树依次入队列 */ public static void cengxuOrder(Node p) { if (null == p) return; Queue<Node> queue = new LinkedList<Node>(); queue.add(p); while (!queue.isEmpty()) { Node temp = queue.poll(); System.out.print(temp + " "); if (temp.getLeft() != null) queue.add(temp.getLeft()); if (temp.getRight() != null) queue.add(temp.getRight()); } } public static void quDongCengxuOrder(Node p) { List<Node> ls=new ArrayList<Node>(); ls.add(p); cengxuOrder(p,ls); } /* * 二叉树层序遍历 递归 对层进行递归 * */ public static void cengxuOrder(Node p, List<Node> nodes) { //空p 或 nodes传进来的值为空 if (p == null || nodes.size()==0) return; ArrayList<Node> ls = new ArrayList<Node>(); if (nodes.size() > 0) { for (Node node : nodes) { System.out.print(node + " "); if (node.getLeft() != null) ls.add(node.getLeft()); if (node.getRight() != null) ls.add(node.getRight()); } } cengxuOrder(p,ls); } /* * 求二叉树深度 递归算法 */ public static int getMaxDepth(Node node) { if(node==null) return 0; int left=getMaxDepth(node.getLeft()); int right=getMaxDepth(node.getRight()); return 1+Math.max(left, right); } /* * 求二叉树深度 非递归算法 */ public static int nonGetMaxDepth(Node node) { if(node==null) return 0; int left=1; int right=1; int max=1; //注意 :一定要node=node.getLeft(); 不然一直是死循环 while(node.getLeft()!=null) { node=node.getLeft(); left++; } while(node.getRight()!=null) { node=node.getRight(); right++; } return Math.max(left, right); }}
- 数据结构--排序-查询-二叉树各种遍历-求深度
- 数据结构--二叉树的遍历--求二叉树的深度(后序遍历)
- 数据结构实验之二叉树的建立与遍历,求二叉树深度
- 八.二叉树各种操作的C语言实现 深度遍历求深度,广度遍历求深度,交换左右子树,求叶子节点数
- java二叉树实现、遍历、求深度
- 数据结构之二叉树(遍历、建立、深度)
- 数据结构之二叉树(遍历、建立、深度)
- 数据结构之二叉树(遍历、建立、深度)
- 数据结构之二叉树(遍历、建立、深度)
- 数据结构之二叉树(遍历、建立、深度)
- 数据结构之二叉树(遍历、建立、深度)
- 数据结构--二叉树--求树的深度的算法(树遍历算法的应用)
- 基本数据结构——二叉树的建立,遍历,求叶子节点,深度计算
- [数据结构]二叉树遍历、求深度C语言的简单实现
- 数据结构基础 各种遍历还原二叉树
- 二叉链表的存储结构和基本操作(各种遍历、求树深度、求树叶个数)
- 二叉树的各种操作 先序 中序 后续 层次 遍历 求树高度 节点深度 知先序中序求后续 二叉排序树
- 二叉树的相关操作:创建、查找、求高度和深度、各种遍历(前、中、后、层序)等等
- 目标跟踪 Object tracking
- 在Acitivity中添加Fragment
- 脚本工具笔记之自动删除废弃资源
- 样例问题 Example question for A4M33MPV course
- js事件绑定及冒泡、捕获和默认行为处理
- 数据结构--排序-查询-二叉树各种遍历-求深度
- 欢迎使用CSDN-markdown编辑器
- OpenCms for MySql安装
- Support in the Wild: My Biggest Elasticsearch Problem at Scale
- 新生代的梦想
- 40个Java 多线程问题总结
- Android_动态权限管理的解决方案
- BZOJ 1015 - 变删点为加点 + 并查集维护
- 开源选型之Android三大图片缓存原理、特性对比