排序算法

  • 衡量排序算法的优劣:

1.时间复杂度:分析关键字的比较次数和记录的移动次数
2.空间复杂度:分析排序算法中需要多少辅助内存
3.稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保 持不变,则称这种排序算法是稳定的。

  • 十大内部排序算法

选择排序
Ø 直接选择排序、堆排序
交换排序
Ø 冒泡排序、快速排序
插入排序
Ø 直接插入排序、折半插入排序、Shell排序
归并排序
桶式排序
基数排序

  • 算法的5大特征
  1. 输入(Input) 有0个或多个输入数据,这些输入必须有清楚的描述和定义
  2. 输出(Output) 至少有1个或多个输出结果,不可以没有输出结果
  3. 有穷性(有限性,Finiteness) 算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤 可以在可接受的时间内完成
  4. 确定性(明确性,Definiteness) 算法中的每一步都有确定的含义,不会出现二义性
  5. 可行性(有效性,Effectiveness) 算法的每一步都是清楚且可行的,能让用户用纸笔计算而求出答案
  • 冒泡排序:
  1. 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
    2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步
    做完后,最后的元素会是最大的数。
    3. 针对所有的元素重复以上的步骤,除了最后一个。
    4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。 ```java public static void main(String[] args) {
    1. int[] arr = new int[]{43,4,26,46,7,85,1,858,48,51,75,25,99};
    2. for(int i=0;i<arr.length-1;i++){
    3. for(int j=0;j<arr.length-i-1;j++){
    4. if(arr[j]>arr[j+1]){
    5. int temp = arr[j];
    6. arr[j] = arr[j+1];
    7. arr[j+1] = temp;
    8. }
    9. }
    10. }
  1. - 快速排序
  2. 是迄今为止所有内排序算法中速度最快的一种。冒泡排序的升级版,交换排序的一种。<br />快速排序的时间复杂度为O(nlog(n))。
  3. - **排序思想:**
  4. - 从数列中挑出一个元素,称为"基准"pivot),
  5. - 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  6. - 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
  7. - 递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代iteration)中,它至少会把一个元素摆到它最后的位置去。
  8. ```java
  9. // 交换函数
  10. private static void swap(int[] array, int i, int j) {
  11. int temp = array[i];
  12. array[i] = array[j];
  13. array[j] = temp;
  14. }
  15. private static void subSort(int[] array, int start, int end) {
  16. //左右指针不相等就继续排序
  17. if (start < end) {
  18. // 基准---数组首元素
  19. int pivot = array[start];
  20. // 待划分区域首元素
  21. int leftPoint = start;
  22. // 待划分区域尾元素----加一是因为下方用的是--rightPoint
  23. int rightPoint = end + 1;
  24. while (true) {
  25. //当左指针还没有移到最右边且指针所指元素小于基准数,继续下次循环
  26. //一直到左指针所指元素大于基准数,记录现在的指针
  27. while (leftPoint < end && array[++leftPoint] <= pivot)
  28. ;
  29. //当右指针还没有移到最左边且指针所指元素大于于基准数,继续下次循环
  30. //一直到右指针所指元素小于基准数,记录现在的指针
  31. while (rightPoint > start && array[--rightPoint] >= pivot)
  32. ;
  33. //记录的左指针是大于基准数的元素
  34. //记录的右指针是小于基准数的元素
  35. //如果此时左指针在右指针左边,交换这两个元素位置
  36. //否则跳出循环----因为此时leftPoint+1=rightPoint,左右指针已经将所有元素已遍历
  37. if (leftPoint < rightPoint) {
  38. swap(array, leftPoint, rightPoint);
  39. } else {
  40. break;
  41. }
  42. }
  43. //交换本轮排序的基准数和最后发现的小于基准数的元素,
  44. //使小于基准数的元素位于左边,大于基准数的元素位于右边
  45. swap(array, start, rightPoint);
  46. //左区域进行下一次排序
  47. subSort(array, start, rightPoint - 1);// 递归调用
  48. //右区域进行下一次排序
  49. subSort(array, rightPoint + 1, end);
  50. }
  51. }
  52. public static void quickSort(int[] array) {
  53. subSort(array, 0, array.length - 1);// 传参----目的数组地址-首指针-尾指针
  54. }
  55. public static void main(String[] args) {
  56. int[] array = { 91, -16, 30, 23, -30, -49, 25, 21, 30 };
  57. System.out.println("排序之前:\n" + java.util.Arrays.toString(array));
  58. quickSort(array);// 传参--目的数组地址
  59. System.out.println("排序之后:\n" + java.util.Arrays.toString(array));
  60. }
  61. }

数组中指定元素的查找:搜索、检索

  • 线性查找:

实现思路:通过遍历的方式,一个一个的数据进行比较、查找。
适用性:具有普遍适用性。

  • 二分法查找

实现思路:每次比较中间值,折半的方式检索。
适用性:(前提:数组必须有序)

  1. int[] arr = new int[]{-84,-79,-36,-8,0,8,12,26,34,48,57,61,85,91,256,364};
  2. int headPoint = 0;
  3. int endPoint = arr.length-1;
  4. int aimNumber = 91;
  5. while(headPoint <= endPoint){
  6. int middle = (headPoint + endPoint)/2;
  7. if(arr[middle] == aimNumber){
  8. System.out.println("找到了!!!第"+(middle+1)+"个元素");
  9. break;
  10. }else if(arr[middle] > aimNumber){//在左边
  11. endPoint = middle - 1;//当未查询到时用于跳出循环
  12. }
  13. else{//在右边
  14. headPoint = middle + 1;//当未查询到时用于跳出循环
  15. }
  16. }
  17. if(headPoint > endPoint){
  18. System.out.println("未找到!!!");
  19. }

排序算法性能对比 :

image.png

  • 从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归 并排序。
  • 从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较简单,将其认为是简单算法。对于Shell排序、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序。
  • 从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排 序、快速排序、 Shell排序和堆排序是不稳定排序
  • 从待排序的记录数n的大小看,n较小时,宜采用简单排序;而n较大时宜采用改进排序。

    排序算法的选择

  • 若n较小(如n≤50),可采用直接插入或直接选择排序。 当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直 接插入,应选直接选择排序为宜。

  • 若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序为宜;
  • 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或 归并排序。