Sorting Algorithms - merge sort and quick sort

Bubble sort, insertion sort, selection sort time complexity of the algorithm are three \ (O (^ n-2) \) , only suitable for small-scale data. Today, we come to know two kinds of time complexity is \ (O (nlogn) \) sorting algorithms - merge sort (Merge Sort) and quick sort (Quick Sort), they have used the partition idea , very clever.

1. merge sort (Merge Sort)?

1.1. Merging Sort Algorithm

  • Merge sort core idea is very simple, if you want to sort an array, we first array from the middle into two parts before and after, before and after the two parts separately and then sorted, then the two parts of the Data Merge row good sequence together on it .

Merge sort

  • Merge sort using a divide and conquer idea, that is, divide and conquer divide and rule, will be a big problem into smaller sub-problems to solve. Divide and conquer algorithms are generally implemented using recursion. Divide and conquer is a problem-solving process thinking, recursion is a programming technique .

  • To array the interval [p, r] are sorted data, we first data into two pieces [P, q] and [q + 1, r], where q is an intermediate position. After two pieces of data sorted, then we have two sub-arrays are combined. When the starting position in the array is less than or equal terminating position described case only one element, recursion will end.
递推公式:
merge_sort(p…r) = merge(merge_sort(p…q), merge_sort(q+1…r))

终止条件:
p >= r 不用再继续分解
  • Process for the two sub-arrays are combined as follows, we first create a temporary array, and then compares the initial start position of the two sub-arrays, a smaller elements into a temporary array, wherein a sub-array comparison until complete and then the rest of the remaining value of the other sub-array all put behind a temporary array. Finally, we need to copy the data in the temporary array into a position corresponding to the original array.

Array merge

  • Code
// O(n(logn))
void Merge_Sort(float data[], int left, int right, float sorted_data[])
{
    if(left < right)
    {
        int mid = (left + right) / 2;
        Merge_Sort(data, left, mid, sorted_data);
        Merge_Sort(data, mid+1, right, sorted_data);
        Merge_Array(data, left, mid, right, sorted_data);
    }
}

void Merge_Array(float data[], int left, int mid, int right, float temp[])
{
    int i = left, j = mid + 1;
    int k = 0;

    // 从子数组的头开始比较
    while(i <= mid && j <= right)
    {
        if (data[i] <= data[j])
        {
            temp[k++] = data[i++];
        }
        else
        {
            temp[k++] = data[j++];
        }
    }

    // 判断哪个子数组还有元素,并拷贝到 temp 后面
    while(i <= mid)
    {
        temp[k++] = data[i++];
    }
    while(j <= right)
    {
        temp[k++] = data[j++];
    }

    // 将 temp 中的数据拷贝到原数组对应位置
    for(i = 0; i < k; i++)
    {
        data[left+i] = temp[i];
    }
}

/*哨兵简化*/
void Merge_Array(float data[], int left, int mid, int right, float temp[])
{
    int max_num = INT_MAX;
    int len = right - left + 1;
    int data_left = new int[mid-left+2];
    int data_right = new int[right-mid+1];
    int i = 0, j = 0, k = 0;

    // 复制左半部分元素,放置哨兵在末尾
    for(int k = left; k <= mid; k++)
    {
        data_left[k-left] = data[k];
    }
    data_left[k-left] = max_num;

    // 复制右半部分元素,放置哨兵在末尾
    for(int k = mid + 1; k <= right; k++)
    {
        data_right[k-mid-1] = data[k];
    }
    data_right[k-mid-1] = max_num;

    for (int k = 0; k < len; k++)
    {
        if (data_left[i] <= data_right[j])
        {
            data[k+left] = data_left[i++];
        }
        else
        {
            data[k+left] = data_right[j++];
        }
    }
}

1.2. Merging Sort Algorithm Analysis

  • 归并排序是一个稳定的排序算法,在进行子数组合并的时候,我们可以设置当元素大小相等时,先将前半部分的数据放入临时数组,这样就可以保证相等元素在排序后依然保持原来的顺序。

  • 不仅递归求解的问题可以写成递推公式,递归代码的时间复杂度也可以写成递归公式
  • 如果我们对 \(n\) 个元素进行归并排序所需要的时间是 \(T(n)\),那分解成两个子数组排序的时间都是 \(T(\frac{n}{2})\),而合并两个子数组的时间复杂度为 \(O(n)\)。所以,归并排序的时间复杂度计算公式为:

    \[ T(1) = C \]
    \[T(n) = 2*T(\frac{n}{2}) + n, n>1\]

  • n = 1 时,只需要常量级的执行时间,所以表示为 C。

    \[T(n) = 2*T(\frac{n}{2}) + n \]
    \[ = 2*[2*T(\frac{n}{4}) + \frac{n}{2}] + n = 4*T(\frac{n}{4}) + 2*n \]
    \[ = 4*[2*T(\frac{n}{8}) + \frac{n}{4}] + 2*n = 8*T(\frac{n}{8}) + 3*n \]
    \[ ......\]
    \[ = 2^k * T(\frac{n}{2^k}) + k * n\]
    \[ ......\]
    \(\frac{n}{2^k} = 1\)时, \(k = log_2n\),代入上式得:
    \[ T(n) = n * C + nlog_2n\]
    用大 O 标记法来表示,归并排序的时间复杂度为 \(O(nlogn)\)

  • 从我们的分析可以看出,归并排序的执行效率与原始数据的有序程度无关,其时间复杂度是非常稳定的,不管是最好情况、最坏情况,还是平均情况,时间复杂度都是 \(O(nlogn)\)

  • 归并排序有一个缺点,那就是它不是原地排序算法。在进行子数组合并的时候,我们需要临时申请一个数组来暂时存放排好序的数据。因为这个临时空间是可以重复利用的,因此归并排序的空间复杂度为 \(O(n)\),最多需要存放 \(n\) 个数据。


2. 快速排序(Quick Sort)?

1.1. 快速排序算法实现

  • 快速排序的思想是这样的,如果要对数组区间 [p, r] 的数据进行排序,我们先选择其中任意一个数据作为 pivot(分支点),一般为区间最后一个元素。然后遍历数组,将小于 pivot 的数据放到左边,将大于 pivot 的数据放到右边。接着,我们再递归对左右两边的数据进行排序,直到区间缩小为 1 ,说明所有的数据都排好了序。
    Quick Sort
递推公式:
quick_sort(p…r) = quick_sort(p…q-1) + quick_sort(q+1, r)

终止条件:
p >= r
  • 归并排序是由下向上的,先处理子数组然后再合并。而快速排序正好相反,它的过程是由上向下的,先分出两个子区间,再对子区间进行排序。归并排序是稳定的时间复杂度为 \(O(n)\),但它是非原地算法,而快排则是原地排序算法。

Merge sort and quick sort

  • 快速排序的分区过程如下所示,从左到右依次遍历数组,如遇到小于 pivot 的元素,则进行数据交换 ,否则继续往前进行,最后再放置 pivot。
    Fast row partition

  • 代码实现
// O(n(logn))
void Quick_Sort(float data[], int left, int right)
{
    if (left < right)
    {
        int i = left, j = left;
        int pivot = data[right];

        for (j = left; j < right; j++)
        {
            if (data[j] < pivot)
            {
                int temp = data[i];
                data[i] = data[j];
                data[j] = temp;
                i++;
            }
        }

        data[j] = data[i];
        data[i] = pivot;
        Quick_Sort(data, left, i-1);
        Quick_Sort(data, i+1, right);
    }
}
  • 快速排序的另一种实现方式如下所示,先取出一个元素作为 pivot(假设是最后一个),这时 pivot 位置可以看作为空,然后从左到右查找第一个比 pivot 大的元素放在 pivot 的位置,此时空的地方变成了这第一个比 pivot 大的元素位置。然后从右到左查找第一个比 pivot 小的元素放在刚才空的位置,依次循环直到从左到右和从右到左都查找到了同一位置,这时候再把 pivot 放置在最后一个空位。这个过程可以形象的被称为“挖坑填坑”。

Quick Sort

  • 代码实现
// O(n(logn))
void Quick_Sort(float data[], int left, int right)
{
    if (left < right)
    {
        int i = left, j = right;
        int pivot = data[j];
        while(i < j)
        {
            while(i < j && data[i] <= pivot) // 从左往右找到第一个比 pivot 大的数
            {
                i++;
            }
            if(i < j)
            {
                data[j--] = data[i];
            }
            while(i < j && data[j] >= pivot) // 从右往左找到第一个比 pivot 小的数
            {
                j--;
            }
            if(i < j)
            {
                data[i++] = data[j];
            }
        }
        data[i] = pivot; // i=j
        Quick_Sort(data, left, i-1);
        Quick_Sort(data, i+1, right);
    }
}

2.2. 快速排序算法分析

  • If each time the quick sort data into two equal parts, the fast discharge time of the same complexity and merge sort is also \ (O (nlogn) \) , but this is difficult to achieve. If the data had already ordered, then each of the partitions are unequal, we need to partition n times to complete the entire ordering, fast row at this time of the time complexity of it devolved into a \ (O (n ^ 2 ) \) .

  • The average time complexity of solving can also be analyzed by a recursive tree is left to us later resolved. We now need to know, in most cases, the time complexity of quick sort of can do \ (O (nlogn) \) , and only in extreme cases, will degenerate into \ (O (n ^ 2) \ ) .

  • Quick Sort is a place sorting algorithm, is an unstable sorting algorithm , because it might change the original position of the equal element in the data exchange process.


3. Summary

  • Merge sort and quick sort are using the idea of ​​divide and conquer, the code is implemented by recursion, the process is very similar.
  • Very stable merge sort, the time complexity is always \ (O (nlogn) \) , but not in situ sequencing; quick sort, although the worst case time complexity is \ (O (the n-^ 2) \) , but the average the complexity of the case at the time \ (O (nlogn) \) , the probability of the worst happening is relatively small, and is the place sorting algorithm, so the application was more widely.

Reference - geeks Time column, "Data Structures and Algorithms beauty"

For more exciting, please pay attention to "seniusen"!
seniusen

Guess you like

Origin www.cnblogs.com/seniusen/p/11979807.html