zoukankan      html  css  js  c++  java
  • 十大排序算法之Java实现

    选择排序

    首先找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。其次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。这种方法我们称之为选择排序

    public class SelectSort {
        public static int[] selectSort(int[] a) {
            int n = a.length;
            for(int i = 0; i < n; i++) {g
                int min = i;
                for(int j = i+1; j < n; j++) {
                    if(a[min] > a[j]) min = j;
                }
    
                 // 交换
                 int temp = a[i];
                 a[i] = a[min];
                 a[min] = temp;
            }
            return a;
        }
    }

    1、 时间复杂度:O(n2)

    2、空间复杂度:O(1)

    3、非稳定排序

    4、原地排序

    插入排序

    插入排序的一个简单理解就是我们平时在打牌时,将每一张牌插入到其他已经有序的牌中的适当位置。当我们给无序的数组做排序时,为了插入元素,我们需要先腾出空间,将其余所有元素在插入之前都向右移动一位,这种算法我们称之为插入排序

    public class InsertSort {
        public static int[] insertSort(int[] arr) {
            if(arr == null || arr.length < 2)
                return arr;
    
            int n = arr.length;
            for(int i = 1; i < n; i++) {
                int temp = arr[i];
                int k = i - 1;
    
                while(k >= 0 && arr[k] > temp)
                    k--;
                // 腾出位置插进去,要插的位置是k+1
                for(int j = i; j > k; j--)
                    arr[j] = arr[j-1];
                
                // 插进去
                arr[k+1] = temp;
            }
            return arr;
        }
    }

    1、 时间复杂度:O(n2)

    2、空间复杂度:O(1)

    3、稳定排序

    4、原地排序

    冒泡排序

    把第一个元素与第二个元素相比较,如果第一个比第二个大,则交换他们的位置。接着继续比较第二个与第三个元素,如果第二个比第三个大,则交换他们的位置...

    非优化版本

    public class BubbleSort {
        public static int[] bubbleSort(int[] arr) {
            if(arr == null || arr.length < 2) {
                return arr;
            }
            int n = arr.length;
            for(int i = 0; i < n; i++) {
                for(int j = 0; j < n; j++) {
                    int t = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = t;
                }
            }
            return arr;
        }
    }

     1、 时间复杂度:O(n2)

    2、空间复杂度:O(1)

    3、稳定排序

    4、原地排序

    优化版本

     假如从开始的第一对到结尾的最后一对,相邻的元素之间都没有发生交换的操作,这意味着右边的元素总是大于等于左边的元素,此时的数组已经是有序的了,我们无需再对剩余的元素重复比较下去了。基于这点我们可以对上述代码做如下优化。

    public class BubbleSort {
        public static int[] bubbleSort(int[] arr) {
            if(arr == null || arr.length < 2) {
                return arr;
            }
            int n = arr.length;
            for(int i = 0; i < n; i++) {
                boolean flag = true;
                for(int j = 0; j < n; j++) {
                    if(arr[j+1] < arr[j]) {
                        flag = false;
                        int t = arr[j+1];
                        arr[j+1] = t;
                    }
                }
                // 遍历一遍下来看是否发生过位置交换
                if(false)
                    break;
            }
            return arr;
        }
    }

    希尔排序

     1 public class ShellSort {
     2     public static int[] shellSort(int arr[]) {
     3         if(arr == null || arr.length < 2) return arr;
     4         int n = arr.length;
     5         // 对每组间隔为h的分组进行排序,刚开始h = n / 2;
     6         for(int h = n/2; h > 0; h /= 2) {
     7             int i = h; i < h; i++) {
     8                 // 将arr[i]插入到所在分组的正确位置上
     9                 insert(arr, h, i);
    10             }
    11         }
    12         return arr;
    13     }
    14     
    15     /**
    16      * 将arr[i]插入到所在分组的正确位置上
    17      * arr[i]所在分组为 ... arr[i-2*h], arr[i-h], arr[i+h] ...
    18     */
    19     private static void insertI(int[] arr, int h, int i) {
    20         int temp = arr[i];
    21         int k;
    22         for(k = i-h; k > 0 && temp < arr[k]; k -= h) {
    23             arr[k+h] = arr[k];
    24         }
    25         arr[k+h] = temp;
    26     }
    27 }

    1、 时间复杂度:O(n2)

    2、空间复杂度:O(1)

    3、非稳定排序

    4、原地排序

    归并排序

    递归式归并排序

     1 public class MergeSort {
     2     public static int[] mergeSort(int[] arr, int left, int right) {
     3         // 如果left==right,表示数组只有一个元素,则不用递归排序
     4         if(left < right) {
     5             // 把大的数组分隔成两个数组
     6             int mid = (left + right) / 2;
     7             // 对左半部分进行排序
     8             arr = mergeSort(arr, left, mid);
     9             // 对右半部分进行排序
    10             arr = mergeSort(arr, mid+1, right);
    11             // 进行合并
    12             merge(arr, left, mid, right);
    13         }
    14         return arr;
    15     }
    16 
    17     // 合并函数,把两个有序的数组合并起来
    18     // arr[left..mif]表示一个数组,arr[mid+1..right]表示一个数组
    19     private static void merge(int[] arr, int left, int mid, int right) {
    20         // 先用一个临时数组把他们合并汇总起来
    21         int[] a = new int[right - left + 1];
    22         int i = left;
    23         int j = mid + 1;
    24         int k = 0;
    25         while(i <= mid && j <= right) {
    26             if(arr[i] < arr[j] {
    27                 a[k++] = arr[i++];
    28             } else {
    29                 a[k++] = arr[j++];
    30             }
    31         }
    32         while(i <= mid) a[k++] = arr[i++];
    33         while(j <= right) a[k++] = arr[j++];
    34         // 把临时数组复制到原数组
    35         for(i = 0; i < k; i++) {
    36             arr[left++] = a[i];
    37         }
    38     }
    39 }

    1、 时间复杂度:O(n2)

    2、空间复杂度:O(n)

    3、稳定排序

    4、原地排序

    非递归式归并排序

     1 public class MergeSort {
     2     // 非递归式的归并排序
     3     public static int[] mergeSort(int[] arr) {
     4         // 子数组的大小分别是1, 2, 4, 8. ...
     5         // 刚开始合并的数组大小是1,接着是2,接着4,...
     6         for(int i = 1; i < n; i += i) {
     7             // 进行数组进行划分
     8             int left = 0;
     9             int mid = left + i - 1;
    10             int right = mid + i;
    11 
    12             // 进行合并,对数组大小为i的数组进行两两合并
    13             while(right < n) {
    14                 // 合并函数和递归式的合并函数一样
    15                 merge(arr, left, mid, right);
    16                 left = right + 1;
    17                mid = left + i - 1;
    18                right = mid + 1;
    19             }
    20 
    21             // 还有一些被遗漏的数组的合并,因为不可能每个数组的大小都刚好为1
    22             if(left < n && mid < n) {
    23                 merge(arr, left, mid, n - 1);
    24             }
    25         }
    26         return arr;
    27     }
    28 }

    快速排序

     我们先从数组中选择一个元素作为中轴元素,然后把数组中所有小于中轴元素的元素放在其左边,所有大于或等于中轴元素的元素放在其右边,显然,此时中轴元素所处的位置是有序。也就是说,我们无需再移动中轴元素的位置。

    然后从中轴元素那里开始把大的数组切割成两个小的数组(两个数组都不包含中轴元素),接着我们通过递归的方式,让中轴元素左边的数组和右边的数组也重复同样的操作,知道数组的大小为1,此时每个元素都处于有序的位置。

     1 public class QuickSort {
     2     public static int[] quickSort(int[] arr, int left, int right) {
     3         if(left < right) {
     4            // 获取中轴元素所处的位置
     5            int mid = partition(arr, left, right);
     6 
     7            // 进行分割
     8            arr = quickSort(arr, left, mid - 1);
     9            arr = quickSort(arr, mid + 1, right);
    10         }
    11         return arr;
    12     }
    13 
    14     private static int partition(int[] arr, int left, int right) {
    15         // 选取中轴元素
    16         int pivot = arr[left];
    17         int i = left + 1;
    18         int j = right;
    19         while(true) {
    20             // 向右找到第一个小于等于pivot的元素位置
    21             while(i <= j && arr[i] <= pivot) i++;
    22             // 向左找到第一个大于的等于pivot的元素位置
    23             while(i <= j && arr[j] >= pivot) j--;
    24             if(i >= j)
    25                 break;
    26             // 交换两个元素的位置,使得左边的元素不大于pivot,右边的不小于pivot
    27             int temp = arr[i];
    28             arr[i] = arr[j];
    29             arr[j] = temp;
    30         }
    31         arr[left] = arr[j];
    32         // 使中轴元素处于有序的位置
    33         arr[j] = pivot;
    34         return j;
    35     }
    36 }

    1、 时间复杂度:O(nlogn)

    2、空间复杂度:O(logn)

    3、非稳定排序

    4、原地排序

    堆排序

    堆的特点就是堆顶的元素是一个最值,大顶堆的堆顶是最大值,小堆顶则是最小值。

    堆排序就是把堆顶的元素与最后一个元素交换,交换之后就破坏了堆的特性。我们再把堆中剩余的元素再次构成一个大顶堆,然后再把堆顶元素与最后第二个元素交换....如此往复下去,等到剩余的元素只有一个的时候,此时的数组就是有序 的了。

     1 public class Head {
     2     // 堆排序
     3     public static int[] headSort(int[] arr) {
     4 
     5         int n = arr.length;
     6         // 构建⼤大顶堆
     7         for (int i = (n - 2) / 2; i >= 0; i--) {
     8             downAdjust(arr, i, n - 1);
     9         }
    10         
    11         // 进⾏行行堆排序
    12         for (int i = n - 1; i >= 1; i--) {
    13             // 把堆顶元素与最后⼀一个元素交换
    14             int temp = arr[i];
    15             arr[i] = arr[0];
    16             arr[0] = temp;
    17             // 把打乱的堆进⾏行行调整,恢复堆的特性
    18             downAdjust(arr, 0, i - 1);
    19         }
    20 
    21         return arr;
    22     }
    23 
    24     // 下沉操作
    25     public static void downAdjust(int[] arr, int parent, int n) {
    26         // 临时保存要下沉的元素
    27         int temp = arr[parent];
    28         // 定位左孩⼦子节点的位置
    29         int child = 2 * parent + 1;
    30         // 开始下沉
    31         while (child <= n) {
    32             // 如果右孩⼦子节点⽐比左孩⼦子⼤大,则定位到右孩⼦子
    33             if(child + 1 <= n && arr[child] < arr[child + 1])
    34                 child++;
    35             // 如果孩⼦子节点⼩小于或等于⽗父节点,则下沉结束
    36             if (arr[child] <= temp ) break;
    37             // ⽗父节点进⾏行行下沉
    38             arr[parent] = arr[child];
    39             parent = child;
    40             child = 2 * parent + 1;
    41         }
    42         arr[parent] = temp;
    43     }
    44 }

     1、 时间复杂度:O(n2)

    2、空间复杂度:O(1)

    3、非稳定排序

    4、原地排序

    基数排序

    非优化版本

    优化版本

    桶排序

     1 public class BucketSort {
     2     public static int[] BucketSort(int[] arr) {
     3         if(arr == null || arr.length < 2) return arr;
     4         int n = arr.length;
     5         int max = arr[0];
     6         int min = arr[0];
     7 
     8         // 寻找数组的最⼤大值与最⼩小值
     9         for (int i = 1; i < n; i++) {
    10             if(min > arr[i])
    11                 min = arr[i];
    12             if(max < arr[i])
    13                 max = arr[i];
    14         }
    15 
    16         // 和优化版本的计数排序⼀一样,弄弄⼀一个⼤大⼩小为 min 的偏移值
    17         int d = max - min;
    18         // 创建 d / 5 + 1 个桶,第 i 桶存放 5*i ~ 5*i+5-1范围的数
    19         int bucketNum = d / 5 + 1;
    20         ArrayList<LinkedList<Integer>> bucketList = new ArrayList<>(bucketNum);
    21 
    22         // 初始化桶
    23         for (int i = 0; i < bucketNum; i++) {
    24             bucketList.add(new LinkedList<Integer>());
    25         }
    26 
    27         // 遍历原数组,将每个元素放⼊入桶中
    28         for (int i = 0; i < n; i++) {
    29             bucketList.get((arr[i]-min)/d).add(arr[i] - min);
    30         }
    31 
    32         // 对桶内的元素进⾏行行排序,我这⾥里里采⽤用系统⾃自带的排序⼯工具
    33         for (int i = 0; i < bucketNum; i++) {
    34             Collections.sort(bucketList.get(i));
    35         }
    36 
    37         // 把每个桶排序好的数据进⾏行行合并汇总放回原数组
    38         int k = 0;
    39         for (int i = 0; i < bucketNum; i++) {
    40             for (Integer t : bucketList.get(i)) {
    41                 arr[k++] = t + min;
    42             }
    43         }
    44         return arr;
    45     }
    46 }

    性质:

    1、时间复杂度:O(n+k)

    2、空间复杂度:O(n+k)

    3、稳定排序

    4、⾮非原地排序

    注:k 表示桶的个数

  • 相关阅读:
    unity3d中获得物体的size
    Quartz中时间表达式的设置-----corn表达式
    .net web 开发平台- 表单设计器 一(web版)
    编写你自己的单点登录(SSO)服务
    2009年末最强梅麻呂3D动画游戏大作 汉化补丁
    程序猿加班到深夜,你经历过没?
    初步理解socket
    几种开源分词工具的比較
    ORM框架
    linux tar.gz zip 解压缩 压缩命令
  • 原文地址:https://www.cnblogs.com/seeyoumiter/p/14166163.html
Copyright © 2011-2022 走看看