排序相关的代码

基本排序

归并排序 
 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
 在这里插入图片描述

package sortdemo;

import java.util.Arrays;

/**
 * Created by chengxiao on 2016/12/8.
 */
public class MergeSort {
    public static void main(String []args){
        int []arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int []arr){
        int []temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
        sort(arr,0,arr.length-1,temp);
    }
    private static void sort(int[] arr,int left,int right,int []temp){
        if(left<right){
            int mid = (left+right)/2;
            sort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
            sort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
            merge(arr,left,mid,right,temp);//将两个有序子数组合并操作
        }
    }
    private static void merge(int[] arr,int left,int mid,int right,int[] temp){
        int i = left;//左序列指针
        int j = mid+1;//右序列指针
        int t = 0;//临时数组指针
        while (i<=mid && j<=right){
            if(arr[i]<=arr[j]){
                temp[t++] = arr[i++];
            }else {
                temp[t++] = arr[j++];
            }
        }
        while(i<=mid){//将左边剩余元素填充进temp中
            temp[t++] = arr[i++];
        }
        while(j<=right){//将右序列剩余元素填充进temp中
            temp[t++] = arr[j++];
        }
        t = 0;
        //将temp中的元素全部拷贝到原数组中
        while(left <= right){
            arr[left++] = temp[t++];
        }
    }
}

链表排序
在这里插入图片描述
要求: O(n log n) 时间复杂度和常数级空间复杂度下
我的做法:
堆排:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
          if(Objects.isNull(head) || Objects.isNull(head.next)){
              return head;
          }
          PriorityQueue<ListNode> queue = new PriorityQueue<>(new Comparator<ListNode>() {
              @Override
              public int compare(ListNode o1, ListNode o2) {
                  return o1.val-o2.val;
              }
          });
        ListNode node = head;
        while (Objects.nonNull(node)){
            queue.add(node);
            node = node.next;
        }
        ListNode node1 = queue.poll();
        ListNode result = node1;
        while (!queue.isEmpty()){
            node1.next = queue.poll();
            node1 = node1.next;
        }
       node1.next = null;
        return result;
    }
}

归并

class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null) return head;
        // 第一步:将链表拆成两半
        ListNode fast = head, slow = head, prev = head;
        while(fast != null && fast.next != null) {
            prev = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        prev.next = null;
        // 第二步:将两部分的链表分别排序
        ListNode l1 = sortList(head);
        ListNode l2 = sortList(slow);
        // 第三步:合并两个有序链表
        return merge(l1,l2);
    }

    private ListNode merge(ListNode l1, ListNode l2) {
        ListNode p = new ListNode(), l = p;
        while(l1 != null && l2 != null) {
            if(l1.val < l2.val) {
                p.next = l1;
                l1 = l1.next;
            } else {
                p.next = l2;
                l2 = l2.next;
            }
            p = p.next;
        }
        if(l1 != null) {
            p.next = l1;
        }
        if(l2 != null) {
            p.next = l2;
        }
        return l.next;
    }
}

快速排序
在这里插入图片描述

在这里插入图片描述
复杂度分析

时间复杂度:O(n)O(n),如上文所述,证明过程可以参考「《算法导论》9.2:期望为线性的选择算法」。
空间复杂度:O(\log n)O(logn),递归使用栈空间的空间代价的期望为 O(\log n)O(logn)。

class Solution {
    Random random = new Random();

    public int findKthLargest(int[] nums, int k) {
        return quickSelect(nums, 0, nums.length - 1, nums.length - k);
    }

    public int quickSelect(int[] a, int l, int r, int index) {
        int q = randomPartition(a, l, r);
        if (q == index) {
            return a[q];
        } else {
            return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index);
        }
    }

    public int randomPartition(int[] a, int l, int r) {
        int i = random.nextInt(r - l + 1) + l;
        swap(a, i, r);
        return partition(a, l, r);
    }

    public int partition(int[] a, int l, int r) {
        int x = a[r], i = l - 1;
        for (int j = l; j < r; ++j) {
            if (a[j] <= x) {
                swap(a, ++i, j);
            }
        }
        swap(a, i + 1, r);
        return i + 1;
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}
class Solution {
    Random rand=new Random();
    public int findKthLargest(int[] nums, int k) {
        return quickSort(nums,k,0,nums.length-1);
    }
    private int quickSort(int[] nums,int k,int left,int right){
        int index=rand.nextInt(right-left+1)+left;
        int flag=nums[index];
        nums[index]=nums[left];
        int i=left,j=right;
        while (i<j){
            while (i<j&&nums[j]<=flag) j--;
            nums[i]=nums[j];
            while (i<j&&nums[i]>=flag) i++;
            nums[j]=nums[i];
        }
        nums[i]=flag;
        if (i==k-1) return nums[i];
        else if (i<k-1) return quickSort(nums,k,i+1,right);
        else return quickSort(nums,k,left,i-1);
    }
}

方法二:基于堆排序的选择方法
复杂度分析

时间复杂度:O(n \log n)O(nlogn),建堆的时间代价是 O(n)O(n),删除的总代价是 O(k \log n)O(klogn),因为 k < nk<n,故渐进时间复杂为 O(n + k \log n) = O(n \log n)O(n+klogn)=O(nlogn)。
空间复杂度:O(\log n)O(logn),即递归使用栈空间的空间代价。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        buildMaxHeap(nums, heapSize);
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            swap(nums, 0, i);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    public void buildMaxHeap(int[] a, int heapSize) {
        for (int i = heapSize / 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        } 
    }

    public void maxHeapify(int[] a, int i, int heapSize) {
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        } 
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a, i, largest);
            maxHeapify(a, largest, heapSize);
        }
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

下面这个跟上面的一样
前 K 个高频元素
也是基于堆排和快排,,,做完上面题目再看下这个思路。

我自己实现的快排
第一次写,一堆坑。
前提:按从大到小排
根据快排思想,就是取某一个值,比它大的放左边,比它小的放右边,最后按照左中右一排;
但我走了很多坑:
1.只分了左右两部分,没有中间,导致比如最小的在最左,每次以第一个数作为比较 值,他会一直在最左,每次分两部分都是一样的,陷入死循环,,后改成三部分就好了。
2.左中右三部分 在分出来之前,,不知道有多少个,而数组初始化的时候就必须指定长度,故好多拷贝。

public  int[] quickSort(int[] array){
     if(Objects.isNull(array) || array.length ==0 ||array.length==1){
         return array;
     }
     //取值
    int mid = array[0];

     int[] leftMore = new int[array.length];
     int[] rightMore = new int[array.length];
     int[] midMore = new int[array.length];
     int leftMoreSize = 0;
     int rightMoreSize = 0;
     int midMoreSize = 0;
     for(int a:array){
         if(a>mid){
             leftMore[leftMoreSize++] = a;
         } else if (a<mid) {
             rightMore[rightMoreSize++] = a;
         } else {
             midMore[midMoreSize++] = a;
         }
     }

     int[] left = new int[leftMoreSize];
     int[] right = new int[rightMoreSize];
     int[] midOne = new int[midMoreSize];
     System.arraycopy(leftMore,0,left,0,leftMoreSize);
     System.arraycopy(rightMore,0,right,0,rightMoreSize);
     System.arraycopy(midMore,0,midOne,0,midMoreSize);

   int[] leftResult = quickSort(left);
   int[] rightResult = quickSort(right);
    int[] result = new int[array.length];
    if(leftMoreSize > 0){
        System.arraycopy(leftResult,0,result,0,leftMoreSize);
    }
    System.arraycopy(midOne,0,result,leftMoreSize,midMoreSize);
    if(rightMoreSize >0){
        System.arraycopy(rightResult,0,result,leftMoreSize+midMoreSize,rightMoreSize);
    }
    return result;
}

我楼上的做法有好多次拷贝,不好,网上拷贝了别人的,空间复杂度为O(1)
我当时写的思路,就是准备额外空间,将左中右分别放在自己的组里,然后对左右无序组再递归排序,这就需要额外空间了。人家的思路是:
取最左值为基准值,所有数据都在他的右侧,然后一个指针从右出发,一个从左出发,右边第一次遇到比他小的值,与左边第一个比他大的值,就交换,如果左右未相遇,就继续缩小范围的走(缩小包围圈,哈哈),每次遇到左大右小就交换,直到相遇,,那我们知道,相遇时,左边的都是比它小的,右边的都是比它大的,再将第一个位置的值也就是基准值与左指针交换位置,此时就形成了,基准值左边的比它小,右边的比它大。
下面的算法有个注意点:
在这里插入图片描述
这两个while的顺序,上面的while可能会多走一步,而下面的不会,所以交换的应该是下面的那个。
在这里插入图片描述
举个例子,如果两个while互换,以case:4,1,5为例,会造成i走到2位置,即5,这时候4和4互换,显然不对

 /**
     * 快速排序
     * @param array
     */
    public static void quickSort(int[] array) {
        int len;
        if(array == null
                || (len = array.length) == 0
                || len == 1) {
            return ;
        }
        sort(array, 0, len - 1);
    }

    /**
     * 快排核心算法,递归实现
     * @param array
     * @param left
     * @param right
     */
    public static void sort(int[] array, int left, int right) {
        if(left > right) {
            return;
        }
        // base中存放基准数
        int base = array[left];
        int i = left, j = right;
        while(i != j) {
            // 顺序很重要,先从右边开始往左找,直到找到比base值小的数
            while(array[j] >= base && i < j) {
                j--;
            }

            // 再从左往右边找,直到找到比base值大的数
            while(array[i] <= base && i < j) {
                i++;
            }

            // 上面的循环结束表示找到了位置或者(i>=j)了,交换两个数在数组中的位置
            if(i < j) {
                int tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }

        // 将基准数放到中间的位置(基准数归位)
        array[left] = array[i];
        array[i] = base;

        // 递归,继续向基准的左右两边执行和上面同样的操作
        // i的索引处为上面已确定好的基准值的位置,无需再处理
        sort(array, left, i - 1);
        sort(array, i + 1, right);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值