五种排序算法的学习

前言

现在计算机行业越来火爆, 因此许多人也加入这滚滚洪流. 本人作为小菜鸡一枚, 略有感悟五种小算法.因此随笔进行记录, 还会配以代码进行理解.
我觉得, talk is cheap, show me the code 才是硬道理.

Bubble Sort (冒泡排序)

Nothing is better than pseudocode.

function bubbleSort(A:list of sortable things)
  let n = length of A
  repeat
    swapped = false
    for i = 1 to n - 1 (inclusive) do
        if A[i - 1] > A[i] then
          swap(A[i - 1] and A[i])
          swapped = true
        end if 
    end for
until not swapped
end of function

伪代码就是如上所示. 那么写成实现代码就好简单了. C++代码如下所示:

// 首先我们写一个bubble 的过程
void bubble(int arr[], int n) {
    int i;
    int temp;
    for (i = 0; i < n ; i++) {
        if (arr[i] > arr[i + 1]) {
            temp = arr[i];
            arr[i] = arr[i + 1];
            arr[i + 1] = temp;
        }
    }
}
// 然后我们可以就来做bubbleSort
void bubbleSort(int arr[], int n) {
    for (int i = n; i >= 1; i--) {
        bubble(arr, i);
    }
}

接下来我们上测试代码。(C++菜鸡, java也是类似, 复制过去跑把。。。。)

int main() {
    int arr[] = {6, 2, 4, 1, 7, 3, 8, 9};
    bubbleSort(arr, 8);
    for (int i = 0; i < 8; i++) {
        cout << arr[i] << " ";
    }
    return 0;
}

运行结果如下:


冒泡排序

我们可以来具体分析一下这个算法, 作为最简单的排序算法, 这个算法时间复杂度是. 这个算法可能是最好掌握的一个方法, 但是我们仔细想想, 这个方法其实速度对于大规模排序来说就很容易挂了. 因此并不是一个常用(好用)的方法.相较于别的的方法, 比如插入排序,冒泡的表现也是比较糟糕的...

SelectionSort (选择排序)

选择排序是一种简单直观的排序算法,无论什么数据进去都是 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。

  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
    重复第二步,直到所有元素均排序完毕。

废话不多说, 我们来看代码怎么写。我们先考虑选择过程

int findMaxPos(int arr[], int n) {
    // 找出数组中最大元素的位置
    int pos = 0; // 假设你是最大的
    int max = arr[0];
    for (int i = 0; i <= n; i++) {
        if (arr[i] >= max) {
            max = arr[i];
            pos = i;
        }
    }
    return pos;
}

找出元素最大的位置之后, 我们来做排序。

void selectionSort(int arr[], int n) {
    while (n > 0) {
        int pos = findMaxPos(arr, n);
        int tmp = arr[n];
        arr[n] = arr[pos];
        arr[pos] = tmp;
        n--;
    }
}

测试代码就自行测试把。
算法分析: 时间复杂度
空间复杂度
表现也比较糟糕。。。。

MergeSort(合并排序)

这个方法是1945年由冯诺依曼这个人实现过.这个方法也是典型的分治法(Divide and Conquer). 作为典型的分治法,我们来看一下这个东西咋弄.

/*MergeSort part.*/
    public void merge(int[] arr, int l, int m, int r) {
        // divide to 2 parts.
        int n1 = m - l + 1;
        int n2 = r - m;
        // then we need to allocate data.
        int[] L = new int[n1];
        int[] R = new int[n2];
        // copy data.
        for (int i = 0; i < n1; i++) {
            L[i] = arr[l + i];
        }
        for (int j = 0; j < n2; j++) {
            R[j] = arr[m + j + 1];
        }

        // then we need to merge.
        int i = 0, j = 0;
        int k = l;
        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) {
                arr[k] = L[i];
                i++;
            } else {
                arr[k] = R[j];
                j++;
            }
            k++;
        }

        while (i < n1) {
            arr[k] = L[i];
            i++;
            k++;
        }
        while (j < n2) {
            arr[k] = R[j];
            j++;
            k++;
        }
    }

    public void mergeSort(int[] arr, int l, int r) {
        if (l < r) {
            int m = (r + l) / 2;
            mergeSort(arr, l, m);
            mergeSort(arr, m + 1, r);
            merge(arr, l, m, r);
        }

    }

这个思路用的是递归(Recursive). 有兴趣的朋友可以思考一下迭代的怎么写.

QuickSort(快速排序)

/*Quick Sort*/
    public int partition(int[] arr, int low, int high) {
        // step 1. find a pivot.
        // let's suppose the pivot is the last one.
        int pivot = arr[high];
        // i is the variable to count, which idx we need to swap later?

        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] <= pivot) {
                i++;
                swap(arr, i, j);
            }
        }
        // after this, we need to swap
        swap(arr, i + 1, high);
        return i + 1;
    }

    public void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            int pivot = partition(arr, low, high);
            quickSort(arr, low, pivot - 1);
            quickSort(arr, pivot + 1, high);
        }

    }

插入排序

首先考虑一个场景: 排序一手扑克牌。开始我们左手为空,桌子的牌朝下。然后, 我们每次从桌子上拿走一张牌, 把他插入到左手中正确的位置。

仔细分析如上场景:

  • 我们左手的牌是始终保持有序的
  • 我们需要逐次查看我们有序的牌, 然后和我们新拿的牌作比较,决定插入位置
    这也是为什么他的名字叫插入排序。
    代码如下:
public void insertionSort(int[] arr) {
        for (int j = 1; j < arr.length; j++) {
            int key = arr[j];
            int i = j - 1; // arr[0] 到 arr[i] 这部分是有序的, 等价上述场景的我们左手的牌

            while (i >= 0 && arr[i] >= key) {
                // 移动位置, 找到牌合适的位置, 插入。
                arr[i + 1] = arr[i];
                i = i - 1;
            }
            arr[i + 1] = key;
        }
    }

测试效果:

int[] arr = {5, 2, 4, 6, 1, 3, 7};

排序之后:


排序后

算法分析

时间复杂度: 。(当数组已经反向排序, 需要正向排序的时候)
空间复杂度: 。

你可能感兴趣的:(五种排序算法的学习)