java -- sorting algorithm

java -- sorting algorithm

 

 

       Searching and sorting algorithms are the introductory knowledge of algorithms, and their classic ideas can be used in many algorithms. Because its implementation code is shorter, the application is more common. Therefore, sorting algorithms and related questions are often asked in interviews. However, it is the same as it is. As long as you are familiar with the idea, it is not difficult to use it flexibly. Generally, the most common tests in interviews are quick sort and merge sort, and interviewers often ask to write the codes of these two sorts on the spot. The code for these two sorts must be handy. There are also insertion sort, bubble sort, heap sort, radix sort, bucket sort, etc.

 

 

Comparison of various sorting algorithms:



 

 

 

 

Insertion sort

 

       Insertion sort is like sorting your own cards when you divide the cards. How do you sort them when there are many cards? It is to get a card and find a suitable place to insert it. This principle is actually the same as insertion sort. Design idea: Before each insertion, check the previous data, if it is smaller than the current data, move one bit backward, and insert the data when it cannot be moved.

 

Take a chestnut:

 

Perform a simple insertion sort on the unordered sequence of 5, 3, 8, 6, and 4. First, assume that the position of the first number is correct. Think about it, when you get the first card, there is no need to sort it out. Then the 3 should be inserted in front of the 5, and the 5 should be moved back one place to become 3, 5, 8, 6, 4. It should be the same when you think about the arrangement of the cards. Then 8 does not move, 6 is inserted in front of 8, 8 is moved back one place, 4 is inserted in front of 5, and it is moved back one place from 5. Note that when inserting a number, make sure that the number in front of the number is in order.

 

 code show as below:

    public static void insertSort(int[] arr) {
        if(arr == null || arr.length == 0)
            return ;

        for(int i=1; i//It is correct to assume the first number position; to move backward, the first one must be assumed.

            int j = i;
            int target = arr[i]; //to be inserted

            // All numbers larger than target are moved backward
            while(j > 0 & target < arr[j-1]) {
                arr[j] = arr[j-1];
                j --;
            }

            //插入 
            arr[j] = target;
        }

    }

 

 

 

 

希尔排序

 

       希尔排序是插入排序的一种高效率的实现,基本思想是:因为数据基本有序的情况下,插入排序是非常快的。根据步长进行两个数据的比较,如果小于就对调;走完一遍后,计算步长,在进行前面的比较;余此类推,当步长为1时,数据已经比较有序了,最后一次进行插入排序。

 

举个栗子:

 

初始数组: [26, 53, 67, 48, 57, 13, 48, 32, 60, 50 ]

 

       
 

结果数组: [13, 26, 32, 48, 48, 50, 53, 57, 60, 67]

 

代码如下:

    public static void shellSort(int data[]) {
        if (data == null) return;
        int j = 0;
        int temp = 0;
        int len = data.length / 2; //计算步长
        for (int increment  = len; increment > 0; increment /= 2) {
            for (int i = increment; i < data.length; i++) {
                temp = data[i];
                for (j = i; j >= increment; j -= increment) {
                    if(temp < data[j - increment]){
                        data[j] = data[j - increment];
                    }else{
                        break;
                    }
                }
                data[j] = temp;
            }
        }
    }

 

 

 

 

选择排序

 

       选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面。但是过程不同,冒泡排序是通过相邻的比较和交换。而选择排序是通过对整体的选择。

 

 

举个栗子:

 

       对5,3,8,6,4这个无序序列进行简单选择排序,首先要选择5以外的最小数来和5交换,也就是选择3和5交换,一次排序后就变成了3,5,8,6,4,第二次就找出比5小的数找到4,找到4与5交换后变成3,4,8,6,5,对剩下的序列一次进行选择和交换,最终就会得到一个有序序列。

 

 

优点:

 

       其实选择排序可以看成冒泡排序的优化,因为其目的相同,只是选择排序只有在确定了最小数的前提下才进行交换,大大减少了交换的次数。

 

 

代码如下:

    public static void selectSort(int[] a) {
        if (a == null) return;
        int min = 0;
        int i = 0;
        int j = 0;
        int index = 0;
        int len = a.length;
        for (i = 0; i < len - 1; i++) {
            min = a[i];
            index = i;
            for (j = i + 1; j < len; j++) {
                if (a[j] < min) {
                    min = a[j];
                    index = j;
                }
            }
            a[index] = a[i];
            a[i] = min;
        }
    }

 

 

 

 

堆排序

 

堆排序是借助堆来实现的选择排序,思想同简单的选择排序。

 

 

堆排序要解决的问题

 

如何由一个无序序列键成一个堆?

如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?

 

问题一

可以直接使用线性数组来表示一个堆,由初始的无序序列建成一个堆就需要自底向上从第一个非叶元素开始挨个调整成一个堆。

 

问题二

首先是将堆顶元素和最后一个元素交换。然后比较当前堆顶元素的左右孩子节点,因为除了当前的堆顶元素,左右孩子堆均满足条件,这时需要选择当前堆顶元素与左右孩子节点的较大者(大顶堆)交换,直至叶子节点。我们称这个自堆顶自叶子的调整成为筛选。

 

代码如下:

    public static void heapSort(int[] array) {
        if (array == null) return;
        buildHeap(array);//构建堆
        int n = array.length;
        int i = 0;
        for (i = n - 1; i >= 1; i--) {
            swap(array, 0, i);
            heapify(array, 0, i);
        }
    }
    //构建
    public static void buildHeap(int[] array) {
        int n = array.length;//数组中元素的个数
        for (int i = n / 2 - 1; i >= 0; i--)
            heapify(array, i, n);
    }
    public static void heapify(int[] A, int idx, int max) {
        int left = 2 * idx + 1;// 左孩子的下标(如果存在的话)
        int right = 2 * idx + 2;// 左孩子的下标(如果存在的话)
        int largest = 0;//寻找3个节点中最大值节点的下标
        if (left < max && A[left] > A[idx])
            largest = left;
        else
            largest = idx;
        if (right < max && A[right] > A[largest])
            largest = right;
        if (largest != idx) {
            swap(A, largest, idx);
            heapify(A, largest, max);
        }
    }
    public static void swap(int[] array, int i, int j) {
        int temp = 0;
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

 

 

 

 

冒泡排序

 

       冒泡排序是最简单的排序之一了,其大体思想就是通过与相邻元素的比较和交换来把小的数交换到最前面。这个过程类似于水泡向上升一样,因此而得名。

 

 

举个栗子:

 

对5,3,8,6,4这个无序序列进行冒泡排序。首先从后向前冒泡,4和6比较,把4交换到前面,序列变成5,3,8,4,6。同理4和8交换,变成5,3,4,8,6,3和4无需交换。5和3交换,变成3,5,4,8,6,3.这样一次冒泡就完了,把最小的数3排到最前面了。对剩下的序列依次冒泡就会得到一个有序序列。

 

    public static void bubbleSort(int[] arr) {
        if(arr == null || arr.length == 0)
            return ;
        for(int i=0; i) {
            for(int j=arr.length-1; j>i; j--) {
                if(arr[j]<arr[j-1]) {
                    swap(arr, j-1, j);
                }
            }
        }
    }

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

 

 

 

 

快速排序

 

       快速排序是冒泡排序的改进版,由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用。特点是,挖坑填数 + 分治法。

 

 

基本思路

 

  1.先从数列中取出一个数作为基准数。

  2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

  3.再对左右区间重复第二步,直到各区间只有一个数。

 


 

对挖坑填数进行总结:

 

  1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。

  2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。

  3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。

  4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。

 

代码如下:

//快速排序 ,l是0,r是数组的length 
void quickSort(int s[], int l, int r) {  
    if (l < r) {
  
        //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 这是其他快速排序可能会用  
        
        // 初始化
        int i = l, j = r, x = s[l];  
        
        while (i < j) { 
            // 从右向左找第一个小于x的数,如果没找到,就一直往左查  
            while(i < j && s[j] >= x)  
                j--;
            // 当找到,就把元素挖出来,填左边的坑,同时右边留一个坑   
            if(i < j) {  
                s[i] = s[j]; 
                // 左边指针向右移一位,后面就要用这个指针对应的数进行比较 
                i++;  
            }  
         
            // 从左向右找第一个大于等于x的数,如果没找到,就一直向右查             
            while(i < j && s[i] < x)   
                i++; 
            // 当找到,就把元素挖出来,填左边的坑,同时右边留一个坑
            if(i < j) {  
                s[j] = s[i]; 
                // 右边指针向左移一位,后面就要用这个指针对应的数进行比较 
                j--;  
            }  
        }
        
        // 用初始值X填上最后剩下的坑  
        s[i] = x;  
        quick_sort(s, l, i - 1); // 递归调用  
        quick_sort(s, i + 1, r);  
    }  
} 

 

 

 

 

归并排序

 

       归并排序是另一种不同的排序方法,因为归并排序使用了递归分治的思想,所以理解起来比较容易。其基本思想是,先递归划分子问题,然后合并结果。把待排序列看成由两个有序的子序列,然后合并两个子序列,然后把子序列看成由两个有序序列。。。。。倒着来看,其实就是先两两合并,然后四四合并。。。最终形成有序序列。



 

代码如下:

    // low是0,high是数组的length
    public static int[] mergeSort(int[] nums, int low, int high) {
        if (nums == null || low < 0 || low > nums.length-1 || high < 0) return nums;
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            mergeSort(nums, low, mid);
            // 右边
            mergeSort(nums, mid + 1, high);
            // 左右归并
            merge(nums, low, mid, high);
        }
        return nums;
    }
    public static void merge(int[] nums, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;
        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                temp[k++] = nums[i++];
            } else {
                temp[k++] = nums[j++];
            }
        }
        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = nums[i++];
        }
        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = nums[j++];
        }
        // 把新数组中的数覆盖nums数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            nums[k2 + low] = temp[k2];
        }
    }

 

 

 

 

 

总结

 

  • 在平均情况下,快速排序最快;
  • 在最好情况下,插入排序和起泡排序最快;
  • 在最坏情况下,堆排序和归并排序最快。

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326488934&siteId=291194637