• 排序算法


    leetcode 总结

    1.冒泡排序

    # 冒泡排序;对相邻的进行比较,把大的往后放,第一轮循环下来可以找到最大的,时间复杂度为O(n^2)
    def bubble_sort(arr):
        for i in range(len(arr)):
            for j in range(len(arr)-1-i):
                if arr[j]>arr[j+1]:
                    temp = arr[j+1]
                    arr[j+1] = arr[j]
                    arr[j] = temp
        return arr
    ret = bubble_sort([1,5,3,2,4,6,2,1,5])
    View Code

    2.插入排序

    # 从下标1开始,依次取出后面的元素和前面的元素相比较,直到元素比左边的大,比右边的小时,则插入;时间复杂度为O(n^2)
    def insertion_sort(arr):
        for i in range(1,len(arr)):  # 从下标1开始向前比较
            loop = i                 # 前面有几个数最大循环几轮
            while(loop):
                if arr[i]<arr[i-1]:
                    arr[i-1], arr[i] = arr[i], arr[i-1]  # 从后往前交换元素
                    i -= 1
                else:
                    break
                loop -= 1
        return arr
    View Code

    二分插入排序(查找的时候使用二分查找,而不是逐位向前查找)

    希尔排序

    希尔排序克服了插入排序中当最小的数在最右边的时候,需要把这个数逐位移到第一位的弊端;

    希尔排序引入了增量序列,对每个增量序列下的子数组进行大间隔插入排序,随着后面的间隔越来越小,数组逐渐趋于部分有序

    import java.util.Arrays;
    import java.util.Random;
    
    public class ShellSorted {
        public static void main(String[] args){
            int[] array = new int[15];
            for(int i=0; i<array.length; i++)
                array[i] = new Random().nextInt(20);
            System.out.println("排序前: " + Arrays.toString(array));
            shell(array);
            System.out.println("排序后: " + Arrays.toString(array));
        }
    
        public static void shell(int[] arr){
            int len = arr.length;
            int h = 1;
            // 构造一个尾数为1的固定递增序列,递增序列有几个数表示要循环几轮
            // 分别对每一轮使用跳跃间隔为h的插入排序, 最后一轮的h=1表示进行一次完整的插入排序
            while(h<len/3) h = 3*h + 1;  // 1, 4, 13...
            while(h>=1){
                for(int i=h; i<len; i++)
                    for(int j=i; j-h>=0 && arr[j]<arr[j-h]; j-=h){
                        int temp = arr[j];
                        arr[j] = arr[j-h];
                        arr[j-h] = temp;
                    }
                h = h/3;
            }
    
            /*
            // 使用不同的递增序列
            for(int k=len/2; k>=1; k=k/2)  
                for(int i=k; i<len; i++)
                    for(int j=i; j-k>=0 && arr[j]<arr[j-k]; j-=k){
                        int temp = arr[j];
                        arr[j] = arr[j-k];
                        arr[j-k] = temp;
                    }
            */
        }
    }
    View Code

    3.选择排序

    # 选择排序,每次找出一个最小值,再把他放到最开始的位置(或加入一个新的数组),时间复杂度 O(n^2)
    def find_min(arr):
        min_num = arr[0]
        for i in arr:
            if i<min_num:
                min_num = i
        return min_num
    def selection_sort(arr):
        newarr = []
        count = len(arr)
        while count:  # 这里虽然改变了列表的长度,但是没影响
            ret = find_min(arr)
            newarr.append(ret)
            arr.remove(ret)
            count -= 1
        return newarr
    # 写法二(不需要额外的空间)
    def selectSort(arr):
        for i in range(len(arr)):
            min = i
            for j in range(i+1, len(arr)):
                if(arr[j]<arr[min]):
                    min = j
            # arr[i] = arr[i] + arr[min]
            # arr[min] = arr[i] - arr[min]  # 如果min没有变的话,此时的 min==i;不要乱用
            # arr[i] = arr[i] - arr[min]
            temp = arr[i]
            arr[i] = arr[min]
            arr[min] = temp
        return arr
    # 写法三
    def selection_sort_2(arr):
        for i in range(len(arr)):
            for j in range(i+1, len(arr)):
                if arr[j] < arr[i]:  # 这个是多次交换,上面的是一次交换;
                    temp = arr[j]
                    arr[j] = arr[i]
                    arr[i] = temp
        return arr
    View Code

    4.快速排序

    # 随机选择一个值,把数组分为小于这个值的部分和大于的部分,再对这两部分递归
    # 快速排序算法的平均复杂度为O(nlog(n))(递归log(n)层,每层为O(n)),最糟复杂度为O(n^2)
    
    def quick_sort(arr):
          if len(arr) < 2: return arr   
          else:
              flag = arr[0]
              bigger = [i for i in arr[1:] if i >= flag]
              smaller = [i for i in arr[1:] if i < flag]
          return quick_sort(smaller) + [flag] + quick_sort(bigger)
    View Code

    快速排序的更优版本,不需要额外的数组空间,只需要递归空间

    import java.util.Arrays;
    
    public class QuickSortDemo {
        public static void main(String[] args){
            int[] src = {1,4,2,5,2,0,3,7,2,3,1};
            QuickSort(src, 0, src.length-1);
            System.out.println("排序后: " + Arrays.toString(src));
        }
    
        private static void QuickSort(int[] arr, int low, int high){
            // Collections.shuffle(Arrays.asList(arr));
            if(low>=high) return;
            // 先进行交换,使j左边的数都小于j,j右边的数都大于j,然后再左右递归
            int j = Partition(arr, low, high);
            QuickSort(arr, low, j-1);
            QuickSort(arr, j+1, high);
        }
    
        private static int Partition(int[] arr, int low, int high){
            int i = low, j = high + 1;
            int k = arr[low];
            while(true){
                while(arr[++i]<k) if(i==high) break; // 找左边大于等于k的数
                while(arr[--j]>k) if(j==low) break;  // 找右边小于等于k的数,j==low的时候循环条件一定为false
                
                if(i>=j) break;  // 先判断是否已经相遇
    
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
            // 此时j右边的数都大于k,j左边的数包括j都小于k,交换位置j和k的值,进入下一轮划分
            int temp = arr[low];
            arr[low] = arr[j];
            arr[j] = temp;
    
            return j;
        }
    }    
    快速排序
    public void quickSort(int[] nums, int left, int right){
        // 插入排序+快速排序
        if(right-left<=7){
            for(int i=left+1; i<right+1; i++){
                int temp = nums[i];
                int j = i;
                while(j>0 && nums[j-1]>temp){
                    nums[j] = nums[j-1];
                    j--;
                }
                nums[j] = temp;
            }
            return;
        }
        int mid = partition(nums, left, right);
        quickSort(nums, left, mid-1);
        quickSort(nums, mid+1, right);
    }
    public int partition(int[] nums, int left, int right){
         // 第二种划分方式,填坑法,要注意p和q的边界
         int p = left, q = right;
         int base = nums[left];
         while(p<q){
             // 先在右边找比base小的填充base,再到左边找比base大的填充刚刚的小位置
             while(p<q && nums[q]>=base){
                 q--;
             }
             // 要先判断p和q的边界,p再后面++就不用判断了
             // if(p<q) nums[p++] = nums[q];
             nums[p] = nums[q];
             while(p<q && nums[p]<base){
                 p++;
             }
             // if(p<q) nums[q--] = nums[p];
             nums[q] = nums[p];
         }
         nums[q] = base;
         return q;
    }
    
    // 选择中间元素作为基准元素
    public void quickSortMiddle(int[] nums, int left, int right){
        int base = nums[(left+right)>>1];
        int p = left, q = right;
        while(p<=q){
            // 左边找比basic大的数
            while(nums[p]<base){
                p++;
            }
            // 右边找比basic小的数
            while(nums[q]>base){
                q--;
            }
            if(p<=q){
                int temp = nums[p];
                nums[p] = nums[q];
                nums[q] = temp;
                p++;
                q--;
            }
        }
        if(left<q){
            quickSortMiddle(nums, left, q);
        }
        if(p<right){
            quickSortMiddle(nums, p, right);
        }
    }
    快排的其他写法

    三向切分的快速排序,把数组分为3段,当数组中有大量重复元素的时候,排序更快

    import java.util.Arrays;
    
    public class QuickSort3Way{
        public static void main(String[] args){
            Character[] arrChar = {'R','B','W','W','R','W','W','B','X','R','W','B','R'};
            Sort3Way(arrChar, 0, arrChar.length-1);
            System.out.println("排序之后: " + Arrays.toString(arrChar));
        }
    
        private static <T extends Comparable<T>> void Sort3Way(T[] arr, int low, int high){
            if(low>=high) return;
            T v = arr[low];
            int p = low, i = low + 1, q = high;
            while(i<=q){
                int flag = arr[i].compareTo(v);
                if(flag>0){        // 大于v的放右边
                    T temp = arr[i];
                    arr[i] = arr[q]; // 交换之后arr[i]和v大小是不确定的,所以i不动,arr[i]继续下一轮比较
                    arr[q--] = temp;
                }
                else if(flag<0){     // 小于v的放左边
                    T temp = arr[i];
                    arr[i++] = arr[p]; // 这时候的arr[i]和v是相等的,所以i++
                    arr[p++] = temp;
                }
                else{   // 等于v的放中间
                    i++;
                }
            }
            // 大于小于v的再递归
            Sort3Way(arr, low, p-1);
            Sort3Way(arr, q+1, high);
        }
    }
    View Code

     5.归并排序

    # 归并排序和快速排序一样,都是分而治之的思想,归并排序的时间复杂度是 O(nlog(n)),并且是一个稳定的排序算法
    # 把一个数组递归分为对等两段,这两段放一起排序并且在排序过程中合并成一个数组
    def mergeSort(arr):
        if len(arr) < 2: return arr
        middle = len(arr)//2
        left = arr[:middle]
        right = arr[middle:]
        return merge(mergeSort(left), mergeSort(right))
    
    # 两个数组的元素比较大小,合并成一个数组
    def merge(left, right):
        mergeTwoArr = []
        i = 0
        j = 0
        while i<len(left) and j<len(right):  # 这里要小于总长度,而不是总长度减1;因为i是后加的,如果是总长度减1不能到最后一个元素
            if(left[i]<=right[j]):
                mergeTwoArr.append(left[i])
                i += 1
            else:
                mergeTwoArr.append(right[j])
                j += 1
        mergeTwoArr += left[i:]    # left或right总有一个下标超出了界限,得到一个空列表,没超出界限的那个比mergeTwoList里面的都大
        mergeTwoArr += right[j:]
        return mergeTwoArr
    View Code

    归并,自顶向下和自底向上

    import java.util.Arrays;
    
    public class MergeSortDemo {
        public static void main(String[] args){
            int[] src2 = {1,4,2,5,2,0,3,7,2,3,1};
            int[] aux = new int[src2.length];
            MergeSortTB(src2, aux, 0, src2.length-1);
            System.out.println("排序后:" + Arrays.toString(src2));
    
            int[] src3 = {1,4,2,5,2,0,3,7,2,3,1};
            int[] aux2 = new int[src3.length];
            MergeSortBU(src3, aux2);
            System.out.println("排序后:" + Arrays.toString(src3));
        }
    
        // 自顶向下
        private static void MergeSortTB(int[] arr, int[] aux, int low, int high){
            if(low>=high) return;
            int mid = (low + high) >>> 1;
            MergeSortTB(arr, aux, low, mid);
            MergeSortTB(arr, aux, mid+1, high);
            if(arr[mid]<arr[mid+1])
                return;
            Merge(arr, aux, low, mid, high);
        }
    
        // 自底向上,只需合并,不用递归
        private static void MergeSortBU(int[] arr, int[] aux){
            int N=arr.length;
            for(int sz=1; sz<N; sz+=sz)  
                for(int low=0; low<N-sz; low += sz+sz)
                    Merge(arr, aux, low, low+sz-1, Math.min(low+sz+sz-1, N-1));
        }
    
        private static void Merge(int[] arr, int[] aux, int low, int mid, int high){
            for(int i=low; i<=high; i++){
                aux[i] = arr[i];   // 把左右两个小数组合并到辅助数组
            }
            // 使用双指针对两个数组进行排序  
            for(int i=low, p=low, q=mid+1; i<=high; i++){
                if(p>mid)                arr[i] = aux[q++];   // 左边的数组排完了
                else if(q>high)          arr[i] = aux[p++];  // 右边的数组排完了
                else if(aux[p]>aux[q])   arr[i] = aux[q++];
                else                     arr[i] = aux[p++];
            }
        }
    }    
    java归并

    归并+插入(当归并到数组长度小于一个指定值时,对这小部分数使用插入排序)

    import java.util.Arrays;
    
    public class MergeSortDemo {
        private static final int INSERTIONSORT_THRESHOLD = 7;
        public static void main(String[] args){
            int[] src = {1,4,2,5,2,0,3,7,2,3,1};
            int[] dest = src.clone();
            MergeSort(src, dest, 0, src.length);
            System.out.println("排序后:" + Arrays.toString(src));
        }
    
        // 归并+插入
        public static void MergeSort(int[] src, int[] dest, int right, int left){
            int len = left - right;
            // 写一个插入排序
            if(len<INSERTIONSORT_THRESHOLD){
                for(int i=right; i<left; i++){
                    for(int j=i; j>right && dest[j]<dest[j-1]; j--){
                        int temp = dest[j-1];
                        dest[j-1] = dest[j];
                        dest[j] = temp;
                    }
                }
                return;
            }
    
            int mid = (right + left) >>> 1;
            MergeSort(src, dest, right, mid);
            MergeSort(src, dest, mid, left); // 递归+插入将左右两边的先各自排好,最后一轮视情况而定
    
            // 如果右边的第一位大于左边的最后一位则直接返回
            if(dest[mid]>dest[mid-1]){
                System.arraycopy(dest, right, src, right, len);
                return;
            }
    
            for(int i=0, p=right, q=mid; i<left; i++){
                // 使用双指针要注意“短路或”问题,对left的判断要放前面才会去判断后面的
                // 如果q指针为true(超出界限), 说明右边处理完了, 接下来要处理p;
                // 如果p指针为false(超出界限), 说明左边处理完了, 接下来要处理q;
                if(q>=left || p<mid && dest[p]<=dest[q]){
                    // System.out.println(p+" "+q);
                    src[i] = dest[p++];
                }
                else{
                    // System.out.println(p+" "+q);
                    src[i] = dest[q++];
                }
            }
        }
    }
    View Code

    6.堆排序

    O(nlogn)的时间复杂度,O(1)的空间复杂度

    class Solution {
        public int[] sortArray(int[] nums) {
            // 堆排序(大根堆),原数组建堆
            heapify(nums); 
    
            // 不断交换堆顶元素和数组尾部的元素
            for(int i=nums.length-1; i>0; i--){
                swap(nums, 0, i);
                // 将堆顶元素下沉到合适位置,后面的不能动了,所以要传入一个界限
                sink(nums, 0, i);
            }
            return nums;
        }
    
    
       public void heapify(int[] nums){
            // 将数组转化为堆,把前半部分元素下沉到合适的位置
            // 注意,要从中间的元素开始下沉,而不是从头部开始
            int n = nums.length;
            for(int i=(n-1)/2; i>=0; i--){
                sink(nums, i, n);
            }
        }
    
        public void sink(int[] nums, int idx, int end){
            int j = 2 * idx + 1;
            while(j<end){
                if(j+1<end && nums[j+1]>nums[j]) j++;
                if(nums[idx]<nums[j]) swap(nums, idx, j);
                else break;
                idx = j;
                j = 2 * idx + 1;
            }
        }
    
        public void swap(int[] nums, int i, int j){
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    
    }    
    堆排序
  • 相关阅读:
    在Salesforce中实现对Object的增删改查操作
    在Salesforce中通过编写C#程序调用dataloadercliq的bat文件取触发调用data loader来批量处理数据
    在Salesforce中通过dataloadercliq调用data loader来批量处理数据
    【LeetCode】189. Rotate Array
    【LeetCode】190. Reverse Bits
    【LeetCode】191. Number of 1 Bits
    【C++】不要想当然使用resize
    【LeetCode】174. Dungeon Game
    【LeetCode】Largest Number
    【DeepLearning】Exercise:Convolution and Pooling
  • 原文地址:https://www.cnblogs.com/pineapple-chicken/p/13637655.html
Copyright © 2020-2023  润新知