C/C++ 十大排序算法(查找算法)&空间复杂度 理论详解

排序

排序是一个非常经典的问题,它以一定的顺序对一个数组(或一个列表)中的项进行重新排序
有许多不同的排序算法,每个都有其自身的优点和局限性。


时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

1. 冒泡排序

  • 1.比较相邻的元素。如果元素大小关系不正确,就交换它们两个;
  • 2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对;
  • 3.针对所有的元素重复以上的步骤,除了最后一个;
  • 4.重复步骤1~3,直到排序完成。

void BubbleSort1(int arr[], int size)
{
    for (int i = 0; i < size - 1; ++i)
        for (int j = 0; j < size - 1 - i; ++j)
            if (arr[j] > arr[j + 1])
                std::swap(arr[j], arr[j + 1]);
}

void BubbleSort2(int arr[], int size)//改进版
{
    int flag = false;
    for (int i = 0; i < size - 1; ++i)
    {
        flag = false;
        for (int j = 0; j < size - 1 - i; ++j)
        {
            if (arr[j] > arr[j + 1])
            {
                std::swap(arr[j], arr[j + 1]);
                flag = true;
            }
        }
        if (!flag) break; // 若没发生交换,则说明数列已有序。
    }
} 

2. 快速排序

  • 1.从待排序序列中挑出一个元素,作为”基准”;
  • 2.把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个过程叫分组或分区
  • 递归地把”基准值前面的子数列”和”基准值后面的子数列”进行排序。不多于一个为止

void QuickSort(int arr[], int left, int right)
{
    while (left < right)
    {
        int i, j, x;
        i = left;
        j = right;
        x = arr[i];
        
        while (i < j)
        {
            while (i < j && arr[j] > x)
                j--; // 从右向左找第一个小于x的数
            if (i < j)
                arr[i++] = arr[j];
            
            while (i < j && arr[i] < x)
                i++; // 从左向右找第一个大于x的数
            if (i < j)
                arr[j--] = arr[i];
        }
        arr[i] = x;//中值位归位
        
        QuickSort(arr, left, i - 1); /* 递归调用 */
        QuickSort(arr, i + 1, right); /* 递归调用 */
    }
} 

3. 插入排序

  • 1.从第一个元素开始,该元素可以认为已经被排序;
  • 2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
  • 3.如果该元素(已排序)大于新元素,将该元素移到下一位置;
  • 4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  • 5.将新元素插入到该位置后;
  • 6.重复步骤2~5。

void InsertSort(int arr[], int size)
{
    int temp;
    int j;
    for (int i = 1; i < size; i++)
    {
        temp = arr[i];
        j = i - 1;
        while (j >= 0 && arr[j] > temp)
        {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
} 

4. 希尔排序

  • 1.对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;
  • 2.对各组内的元素进行直接插入排序,这一趟排序完成之后,每一个组的元素都是有序的;
  • 3.减小gap的值,并重复执行上述的分组和排序,当gap=1时,整个数列就是有序的。

void ShellSort(int* arr, int size)
{
    int temp;
    int j;
    // gap为步长,每次减为原来的一半。
    for (int gap = size / 2; gap > 0; gap /= 2)
    {
        // 共gap个组,对每一组都执行直接插入排序
        for (int i = gap; i < size; ++i)
        {
            temp = arr[i];
            j = i - gap;
            while (j >= 0 && temp < arr[j])
            {
                arr[j + gap] = arr[j];
                j -= gap;
            }
            arr[j + gap] = temp;
        }
    }
} 

5. 选择排序

  • 1.未排序的数列中找到最小(or最大)元素,然后将其存放到数列的起始位置;
  • 2.从剩余未排序的元素中继续寻找最小(or最大)元素,然后放到已排序序列的末尾;
  • 3.以此类推,直到所有元素均排序完毕;

void SelectSort(int* arr, int size)
{
    for (int i = 0; i < size - 1; ++i)//有序区的末尾位置
        for (int j = i + 1; j < size; ++j)//无序区的起始位置
            if (arr[i] > arr[j])
                swap(arr[i],arr[j]);
}

6. 归并排序

  • 1.把长度为n的输入序列分成两个长度为n/2的子序列;
  • 2.对这两个子序列分别采用归并排序;
  • 4.将两个排序好的子序列合并成一个最终的排序序列。

 

void Merge(int* arr, int left, int mid,int right)
{
    int low = left; //左半部分起始位置
    int hight = mid + 1; //右半部分起始位置
    int length = right - left + 1;

    vector<int> temp(length);//辅助数组
    int index = 0; // 辅助数组的下标
    while (low <= mid && hight <= right)
    { //挑选两部分中最小的元素放入辅助数组中
        if (arr[low] < arr[hight])
            temp[index++] = arr[low++];
        else
            temp[index++] = arr[hight++];
    }
//如果还有剩余,直接放入到辅助数组中
    while (low <= mid)
        temp[index++] = arr[low++];
    while (hight <= right)
        temp[index++] = arr[hight++];
//更新原始数组元素
    for (int i = 0; i < length; i++)
    {
        arr[left + i] = temp[i];
    }
}

void MergeSort(int* arr, int left, int right)
{
    if (left < right)
    {
        int mid = (left + right) / 2; //分割序列
        MergeSort(arr, left, mid); //对序列左半部分进行归并排序
        MergeSort(arr, mid + 1, right); //对序列右半部分进行归并排序
        Merge(arr, left, mid, right); //合并已经有序的两个序列
    }
} 

7. 桶排序

  • 1.设置一个定量的数组当作空桶;
  • 2.遍历输入数据,并且把数据一个一个放到对应的桶里去;
  • 3.对每个不是空的桶进行排序;
  • 4.从不是空的桶里把排好序的数据拼接起来。

void BucketSort(int* arr, int size, int max)
{
//创建一个容量为max的数组buckets,并且将buckets中所有数据都初始化为0
    vector<int> buckets(max);
//1.计数
    for (int i = 0; i < size; ++i)
        buckets[arr[i]]++;
//2.排序
    for (int i = 0, j = 0; i < max; ++i)
    {
        while (buckets[i]>0)
        {
            arr[j] = i;
            j++;
            buckets[i]--;
        }
    }
} 

查找

根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素

1. 顺序查找

关键字与队列中的数从最后一个开始逐个比较,直到找出与给定关键字相同的数为止

int SequenceSearch(int* arr,int len,int value)
{
    for (int i = 0; i < len; ++i)
    {
        if (value == arr[i])
            return i;
    }
    return -1;
} 

缺点:效率低下
时间复杂度:最坏情况下,关键词比较次数为,最好情况就是1,所以时间复杂度为O(logn)

2. 二分查找

首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

缺点:要求待查表为有序表
时间复杂度:最坏情况下,关键词比较次数为,最好情况就是1,所以二分查找的时间复杂度为

3. 插值查找

基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率

//递归
int BinarySearch1(int* arr, int value, int low, int high)
{    
    int mid = low + (high - low) / 2;
    if (arr[mid] == value)
        return mid;
    if (arr[mid]>value)
        return BinarySearch1(arr, value, low, mid - 1);
    
    return BinarySearch1(arr, value, mid + 1, high);

}

int BinarySearch2(int* arr, int value, int low, int high)
{
    int mid;
    while (low <= high)
    {
        mid = (low + high) / 2;
        if (arr[mid] == value)
            return mid;
        if (arr[mid]>value)
            high = mid - 1;
        if (arr[mid]<value)
            low = mid + 1;
    }
    return -1;
} 
int InsertionSearch(int* arr, int value, int low, int high)
{
// (查找值索引-low)/value-arr[low]
// = (hith-low)/arr[hith]-arr[low]
    int index = low + (value - arr[low]) / (arr[high] - arr[low])*(high - low);
    if (arr[index] == value)
        return index;
    if (arr[index]>value)
        return InsertionSearch(arr, value, low, index - 1);

    return InsertionSearch(arr, value, index + 1, high);

} 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值