排序算法

落花浮王杯 提交于 2020-03-22 10:50:37

快速排序(基于交换)

特性

每次使一个数归位。不稳定

思想

每次partition划分数组为两部分并返回下标。随后递归地对左右序列快速排序。

代码

int Partition(int arr[],int left,int right)
{
    int i = left;
    int j = right;
    int pivotindex = left; //基准坐标可以任意选
    int pivotnum = arr[pivotindex]; //基准数
    cout << "pivotnum = " << pivotnum << endl;
    while(i < j) //当i==j的时候,i和j的下标就是基准数的正确位置。
    {
        while (i < j && arr[j] >= pivotnum) //先从后面往前找小于基准数
            j--;
        arr[i] = arr[j];   //复制到前面i的位置,不用交换
        while (i < j && arr[i] <= pivotnum)  //再从前往后找大于基准数的值
            i++;
        arr[j] = arr[i]; //复制到后面j的位置。
    }
    arr[i] = pivotnum;  //i,j的位置就是要找的基准数所在的位置。至此数组划分完毕。
    return i;
}
void QuickSort(int arr[], int left,int right)
{
    if (left == right)
        return;
    
        int index = Partition(arr, left, right);
        if(index>left)
        QuickSort(arr, left, index - 1);
        if(index<right)
        QuickSort(arr, index + 1, right);
    
 }

冒泡排序(交换排序)

思想:

两两比较,每次使最小的元素不断往上浮。

特点:

时间复杂度O(N)~O(N^2),空间复杂度为O(1)。稳定,每趟排序使得一个最小的元素归位,全局有序。

代码:

oid bubbleSort(int arr[], int len)
{
    for (int i = 0; i < len-1; i++)
    {
        int flag = 0;//flag记录此次排序有没有交换数字
        for (int j = len - 1; j >= i+1; j--)//从后往前冒泡
        {
            if (arr[j] < arr[j - 1])
            {
                swap(arr[j], arr[j - 1]);
                flag = 1;
            }
        }
        if (flag == 0) //如果本次排序没有交换数字,则表示全局有序。
            return;
    }
}

直接插入排序(插入排序)

思想:

将后面无序数组中的一个待排序的数插入到前面已排序的序列中去。找一个无序的数字,在前面有序的序列中找到插入的位置,移动元素,插入。

特点:

稳定,适用于链表。时间复杂度 O(N)~O(N^2),空间复杂度:O(1)。适用于基本有序或规模小的数组。

代码:

void InsertSort(int arr[], int len)
{
    int i, j;
    for (i = 1; i < len; i++) //从第二个数字开始往前插入。
    {
        int temp;  //保存将要往前插入的数字。
        if (arr[i] < arr[i - 1])//若这个数字本身和前面有序数组放一起是有序的,则跳过。
        {
            j = i;
            temp = arr[i];
            for (; j>0&&temp < arr[j-1]; j--) //从后面往前找插入的位置
            {
                arr[j] = arr[j-1]; //直接覆盖
            }
            arr[j] = temp;
        }
    }
}

折半插入排序(插入排序)

思想:

相比于直接插入排序,折半插入排序将比较和移动操作分离。查找插入的位置用二分法。

特点:

减少了比较的次数,但移动的次数没变,时间复杂度仍未O(N^2)。

代码:

void InsertSort2(int arr[], int len)
{
    int i, j;
    for (i = 1; i < len; i++)
    {
        int temp;
        if (arr[i] < arr[i - 1])
        {
            
            temp = arr[i];
            int low = 0;
            int high = i - 1;
            
            while (low <=high)
            {
                int mid = (low + high) / 2;
                if (arr[i] > arr[mid])
                {
                    low = mid + 1;
                }
                else if (arr[i] < arr[mid])
                {
                    high = mid - 1;
                }
            }
            for (j = i-1; j >= high+1 ; j--)
            {
                arr[j + 1] = arr[j];
            }
            arr[high + 1] = temp;
        }
    }
}

希尔排序(插入排序)

思想:

分为若干子表分别直接插入排序,最后当总体有序时进行一次直接插入排序。步长逐步减少。不能用于链表。

特点:

时间复杂度:O(N^1.3)(取决于选择的序列),时间复杂度:O(1)。不稳定,不能用于链表。

代码:

void ShellSort(int arr[], int len)
{
    for (int gap = len / 2; gap >= 1; gap = gap / 2) //步长逐渐减小
    {
        for (int i = gap; i < len; i++) //并不需要排完一个子序列再去排下一个子序列。
        {
            if (arr[i] < arr[i - gap]) //无序,说明需要插入到前面的有序数组中
            {
                int temp = arr[i];
                int j;
                for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap)//找插入位置
                    arr[j + gap] = arr[j]; //移动元素。
                arr[j + gap] = temp;
            }
        }
    }
}

简单选择排序:

思想:

每次在待排序列表中选一个最小的排到前面去。

特点:

与初始序列无关,时间复杂度始终为O(N^2),空间复杂度为O(1)。不稳定.移动次数小,但是比较次数不变。

代码:

void selectsort(int arr[], int len)
{
    for (int i = 0; i < len - 1; i++)//待排序的数组大小越来越小
    {
        int min = i;  //保存最小数的下标
        for (int j = i + 1; j < len; j++)
        {
            if (arr[j] < arr[min])
                min = j;
            
        }
        if (min != i)
            swap(arr[i], arr[min]); //交换数字
    }
}

堆排序 (选择排序)

思想:按照层次遍历的顺序标号映射到数组中。

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
建立初始堆,输出堆顶元素,将堆底元素送入堆顶,向下调整。

特点:

建堆时间为O(N),之后又n-1次向下调整,每次为O(logN),所以时间复杂度始终为O(NlogN)。不稳定。
删除堆顶元素:把它与堆的最后一个元素交换,向下调整。
插入操作:把新的节点放到堆的末尾,向上调整。

代码:

void adjust(int arr[], int len, int index)
{
    int left = 2 * index + 1; // index的左子节点
    int right = 2 * index + 2;// index的右子节点

    int maxIdx = index;
    if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
    if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;  找左右子节点最大的数交换

    if (maxIdx != index)  //如果将要发生交换
    {
        swap(arr[maxIdx], arr[index]);
        adjust(arr, len, maxIdx);
    }

}

// 堆排序
void heapSort(int arr[], int size)
{
    // 构建大根堆(从最后一个非叶子节点向上)
    for (int i = size / 2 - 1; i >= 0; i--)
    {
        adjust(arr, size, i);
    }

    // 调整大根堆
    for (int i = size - 1; i >= 1; i--)
    {
        swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
        adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
    }
}

归并排序

特性

时间复杂度一直是O(nlogn),稳定,需要辅助数组

思想

采用分治的思想,将数组分为两段,分别归并排序,再合并。

代码

void Merge(int arr[], int l, int mid, int r)  //合并两个数组
{
    int* help = new int[r - l + 1];  //辅助数组
    int p1 = l;   
    int p2 = mid + 1;
    int index = 0;
    while (p1 <= mid && p2 <= r)
    {
        help[index++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    }
    while (p1 <= mid)  //复制剩余的元素
    {
        help[index++] = arr[p1++];
    }
    while (p2 <= r)
    {
        help[index++] = arr[p2++];
    }
    for (int i = 0; i < r - l + 1; i++) //复制到原数组
    {
        arr[l+i] = help[i];
    }
    delete[] help;
}
void MergeSort(int arr[], int l, int r)
{
    if (l == r)
        return;
    if (l < r)
    {
        int mid = (l + r) / 2;
        MergeSort(arr, l, mid);
        MergeSort(arr, mid+1, r);
        Merge(arr, l, mid, r);
    }
    
}

基数排序

思想:

有LSD(最高位优先),MSD(最高位优先),先按某位数的不同分配到桶中,再收集起来,接着再按下一位分配和收集。

特点:

不依靠比较来排序,稳定。时间复杂度O(d(n+radix))。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!