常用排序算法

 

1.冒泡排序

基本思想:从前向后(从下标较小的元素开始)依次比较相邻元素的值,若发现逆序则交换,使值相对较大的元素逐渐从前移动到后部。

优化:

在排序的过程中,各元素不断接近自己的位置,如果一趟下来没有进行过交换,就说明序列有序,因此在排序过程中设置一个标志flag判断元素是否被交换过,从而减少不必要的比较。

class Solution {
    public int[] sortArray(int[] nums) {
        Boolean isFlag = true;
        int i,j,temp;
        for(i = 1;i < nums.length && isFlag;i++){
            isFlag = false;
            for(j = nums.length - 1;j >= i;j--){
                if(nums[j - 1] > nums[j]){
                    temp = nums[j];
                    nums[j] = nums[j - 1];
                    nums[j - 1] = temp;
                    isFlag = true;
                }
            }
        }
        return nums;
    }
}

2.插入排序

基本思想:把n个待排序的元素看成一个有序表和一个无序表。开始时有序表中只包含一个元素无序表中包含n-1个元素。排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将其插入到适当位置。

class Solution {
    public int[] sortArray(int[] nums) {
       int inVal,index;
       for(int i = 1;i < nums.length;i++){
           inVal = nums[i];//待插入的值
           index = i - 1;//待插入的前一个
           while(index >= 0 && inVal < nums[index]){//一直向前寻找比当前待插入值小的之后插入,然后将待插入的位置的值依次赋值给后一个
               nums[index + 1] = nums[index];
               index--;//往前找
           }
           if(index + 1 != i){//如果不是插在当前位置
               nums[index + 1] = inVal;//就将待插入的值赋给插入的位置
           }
       }
        return nums;
    }
}

3.选择排序

基本思想:第一个从arr[0] - arr[n-1]中选取最小值,和arr[0]交换;第二次从arr[1]-arr[n-1]中选取最小值,与arr[1]交换;第三次从arr[2]-arr[n-1]中选取最小值,与arr[2]交换;······一直到第n-1此从arr[n-2]-arr[n-1]中选取最小值,与arr[n-2]交换,。得到一个按排序吗从小到大排列的有序序列

class Solution {
    public int[] sortArray(int[] nums) {
       int min,index;
       for(int i = 0;i < nums.length;i++){
          min = nums[i];
          index = i;
           for(int j = i + 1;j < nums.length;j++){
               if(min > nums[j]){
                    min = nums[j];
                    index = j;
               }
           }
           if(index != i){//如果最小值不是当前值再交换
               nums[index] = nums[i];
               nums[i] = min;
           }
       }
        return nums;
    }
}

4.希尔排序

设置初始增量gap = arr.length / 2;对其分别进行直接插入排序,然后缩小增量gap = gap/2继续进行直接插入排序········

class Solution {
    public int[] sortArray(int[] arr) {
        for(int gap = arr.length / 2; gap > 0;gap /= 2){
            for(int i = gap;i < arr.length;i++){
                int j = i;
                int temp = arr[j];
                if(arr[j] < arr[j - gap]){
                    while(j - gap >= 0 && temp < arr[j - gap]){
                        arr[j] = arr[j - gap];
                        j -= gap;
                    }
                    arr[j] = temp;
                }
            }
        }
        return arr;
    }
}

5.快速排序

对冒泡排序的一种改进,基本思想就是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分所有数据都比另外一部分所有数据都要小,然后再按此方法对这两部分分别进行快速排序,整个排序过程递归进行。

1)数组的快排

//数组的快排
class Solution {
    public int[] sortArray(int[] arr) {
        quickSort(arr,0,arr.length - 1);
        return arr;
    }
    public void quickSort(int[] arr,int left,int right){
        int l = left;//左下标
        int r = right;//右下标
        int mid = arr[(left +right) / 2];//中轴值
        int temp = 0;
        //while循环比mid大的放右边,小的放左边
        while(l < r){
            //左边一直找,找到比mid大的就退出
            while(arr[l] < mid) l++;
            //右边一直找,找到比mid小的就退出
            while(arr[r] > mid) r--;
            if(l >= r) break;//全部找完,左边全小于mid,右边全大于mid
            //交换arr[l] arr[r] 
            temp= arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            if(arr[l] == mid) r--;
            if(arr[r] == mid) l++;//防止数组越界
        }
        if(l == r) {l++; r--;}//
        if(left < r) quickSort(arr,left,r);//向左递归
        if(right > l) quickSort(arr,l,right);  //向右递归
    }
}

2)链表的快排

//迭代法
public ListNode sortList(ListNode head) {
      if(head == null) return null;
       //快速排序
       ListNode curr = new ListNode(0,head);
       ListNode slow = head;
       ListNode fast = head.next;
       ListNode tmp = null;
       while(fast != null){
           if(slow.val <= fast.val){//待排节点值大于已排序好的末尾的节点值,
                tmp = fast.next;
                slow = fast;//就直接放到末尾节点的后面,
                fast = tmp;//然后向后移动继续遍历
                continue;
           }//否则:待排的节点值小于不已经排好的末尾节点值
           ListNode pre = curr;//排好的首端
           ListNode nxt = curr.next;//用于保存待插入的下一个节点值
           ListNode nxfast = fast.next;//保存未排序的节点
           while(nxt != fast){//遍历到第一个未排序的节点之前
               if(fast.val < nxt.val){//如果找到了插入的位置
                   slow.next = fast.next;//先将已排序的末端和待排序的首端连接好
                   fast.next = nxt;//将待排序的节点插入
                   pre.next = fast;
                   break;
               }//没找着就接着遍历往后找
               pre = nxt;
               nxt = nxt.next;
           }
           fast = nxfast;//最后把fast跳到未排序的首端,继续下一次遍历
          
       }
       return curr.next;
    }

//递归法------力扣超时
class Solution {
    public ListNode sortList(ListNode head) {
      //快排2
      if(head == null || head.next == null) return head;
      ListNode dummy = new ListNode(-1);
      dummy.next = head;
      return quickSort(dummy,null);
    }

    private ListNode quickSort(ListNode head,ListNode end){
        if(head == end || head.next == end || head.next.next == end) return head;
        ListNode tmphead = new ListNode(-1);//临时链表
        ListNode mid = head.next;//分割点
        ListNode right = mid;//待排链表指针
        ListNode left = tmphead;//临时链表指针
        while(right.next != end){//将小于划分点的值存储在临时链表中
            if(right.next.val < mid.val){
                left.next = right.next;
                left = left.next;
                right.next = right.next.next;
            }else{
                right = right.next;
            }
        }
        left.next = head.next;//合并临时链表和原链表
        head.next = tmphead.next;//将临时链表插回源链表
        quickSort(head,mid);//向左递归
        quickSort(mid,end);//向右递归
        return head.next;
    }
}

6.归并排序

1)数组的归并

class Solution {
    public int[] sortArray(int[] arr) {
        int[] temp = new int[arr.length];
       if(arr.length == 0) return new int[0];
        mergeSort(arr,0,arr.length - 1,temp);
        return arr;
    }
    //分——合
    public void mergeSort(int[] arr,int left,int right,int[] temp){

        if(left < right){
            int mid = (left + right) / 2;//中间索引
            mergeSort(arr,left,mid,temp);//左递归进行分解
            mergeSort(arr,mid + 1,right,temp);//右递归进行分解
            merge(arr,left,mid,right,temp);//合并
        } 
    }
    //合
    public void merge(int[] arr,int left,int mid,int right,int[] temp){
        
        int i = left; //左边序列的初始索引
        int j = mid + 1; //右边序列的初始索引
        int t = 0;//temp的当前索引
        //将左右两边的有序数据按顺序填充到temp,直到有一边处理完毕为止
        while(i <= mid && j <= right){
            if(arr[i] <= arr[j]){
                temp[t] = arr[i];
                t++;
                i++;
            }else{
                temp[t] = arr[j];
                t++;
                j++;
            }
        }
        //将剩余一侧的数据继续填充
        while(i <= mid){
            temp[t] = arr[i];
            t++;
            i++;
        }
        while(j <= right){
            temp[t] = arr[j];
            t++;
            j++;
        }
        //将temp数组拷贝到arr,并不是拷贝所有
        t = 0;
        int tempLeft = left;
        while(tempLeft <= right){
            arr[tempLeft] = temp[t];
            t++;
            tempLeft++;
        }
    }
}

2)链表的归并

class Solution {
    public ListNode sortList(ListNode head) {
      return head == null ? null : mergeSort(head);
    }
    //归并排序-----“分”
    public ListNode mergeSort(ListNode head){
        if(head == null || head.next == null) return head;//没有节点或者只有一个节点
        //1.快慢指针找中间节点
        ListNode slow = head,fast = head.next;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode left,right;
        right = mergeSort(slow.next);//对右边部分进行归并排序
        slow.next = null;//左边部分的结束标志,末尾节点.next==null
        left = mergeSort(head);//对左半部分进行归并排序
        return mergeList(left,right);//合并左右两部分链表返回
    }
    //合并两个有序链表-----“治”
    ListNode mergeList(ListNode l,ListNode r){
        ListNode dummy = new ListNode(-1);
        ListNode tmp = dummy;
        while(l != null && r != null){
            if(l.val < r.val){
                tmp.next = l;
                l = l.next;
            }else{
                tmp.next = r;
                r = r.next;
            }
            tmp = tmp.next;
        }
        tmp.next = l == null ? r : l;
        return dummy.next;
    }
}

7.桶排序

class Solution {
    public int[] sortArray(int[] arr) {
        if(arr.length == 0) return new int[0];
       int max = Integer.MIN_VALUE;
       for(int i = 1;i < arr.length;i++){
           if(Math.abs(arr[i]) > max){
               max = arr[i];
           }
       }
       if(max < 0) max = -max;
       int maxLength = (max + "").length();//最大数的位数
       int[][] bucket = new int[19][arr.length];//定义桶
       int[] bucketElementCount = new int[19];//记录每个桶中存放多少个数据
       for(int i = 0,n = 1;i < maxLength;i++,n*=10){//对每一位进行排序
           for(int j = 0;j < arr.length;j++){
               int arri = arr[j] / n % 10;//取出每个元素对应位的值放入桶中
               bucket[arri + 9][bucketElementCount[arri + 9]++] = arr[j];
           }
           int index = 0;
           //按照桶的顺序,遍历放入到原来的数组中
           for(int k = 0;k < 19;k++){
               if(bucketElementCount[k]!=0){//桶中有数据才放到原数据
                    for(int l = 0;l < bucketElementCount[k];l++){
                        arr[index++] = bucket[k][l];
                    }
               }
               bucketElementCount[k] = 0; //将每个计数的清零
           }
           index = 0;
       }
        return arr;
    }
}

8.堆排序

:具有以下性质的完全二叉树:

大顶堆:每个节点的值都大于或者等于左右孩子节点的值

 

小顶堆:每个节点的值都小于等于其左右孩子节点的值

思想

升序采用大顶堆,降序采用小顶堆

以升序为例

1)将待排序序列构造成一个大顶堆,此时最大值就是堆顶的根节点

2)将堆顶元素和末尾元素交换,此时末尾元素就是最大值

3)然后将剩余n-1个元素重新构造一个堆,得到n个元素的次小值,反复执行,就得到一个有序序列了。

public void heapSort(int[] arr){
    int tmp = 0;
    //构建大顶堆或者小顶堆
    for(int i = arr.length / 2 - 1;i >= 0;i--){
        adjustHeap(arr,i,arr.length);
    }
    //将堆顶元素和末尾元素交换,将最大元素“沉”到数组末端
    for(int j = arr.length - 1;j > 0){
        tmp = arr[j];
        arr[j] = arr[0];
        arr[0] = tmp;
        adjustHeap(arr,0,j);
    }
}
//将数组调整成大顶堆
void adjustHeap(int[] arr,int i,int length){
    int tmp = arr[i];
    for(int k  = i * 2 + 1;k < length;k=k * 2 + 1){
        if(k + 1 < length && arr[k] < arr[k + 1]) k++;左子节点的值小于右子节点的值,指向右子节点 
        if(arr[k] > tmp){//子节点大于父结点
        arr[i] = arr[k];//将较大的值赋给当前结点
        i = k;//指向k,继续进行循环比较
    	}else{
        	break;
    	}
    }
    arr[i] = tmp;//for循环结束,我们已将以i为父结点的树的最大值放在了最顶端(局部的)
}

9.基数排序

/*
堆排序的思想借助于二叉堆中的最大堆得以实现。首先,将待排序数列抽象为二叉树,并构造出最大堆;
 然后,依次将最大元素(即根节点元素)与待排序数列的最后一个元素交换(即二叉树最深层最右边的叶子结点元素);
 每次遍历,刷新最后一个元素的位置(自减1),直至其与首元素相交,即完成排序。
 */
class Solution {
    public int[] sortArray(int[] arr) {
       int size = arr.length;
       for(int i = size/ 2 -1;i >= 0;i--){
           adjust(arr,size,i);
       }
       for(int i = size - 1;i >= 1;i--){
           int temp = arr[0];
           arr[0] = arr[i];
           arr[i] = temp;
           adjust(arr,i,0);
       }
       return arr;
    }
    public void adjust(int[] nums,int len,int index){
        int l = 2 * index + 1;
        int r = 2 * index + 2;
         int maxIndex = index;
        if (l<len&&nums[l]>nums[maxIndex])maxIndex = l;
        if (r<len&&nums[r]>nums[maxIndex])maxIndex = r;
        if (maxIndex != index) {
            int temp = nums[maxIndex];
            nums[maxIndex] = nums[index];
            nums[index] = temp;
            adjust(nums, len, maxIndex);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值