快速排序

文章目录

快速排序

  1. 思路
    快速排序与归并排序类似,也是使用了分而治之的思路,即在数组中随机寻找一个元素,称为“主元”,然后将数组中其余元素分为两个子集,子集1中元素比主元小,放在主元左边;子集2中元素比主元大,放在主元右边,该过程称为“子集划分”。将两个子集递归地“找主元”和“子集划分”,当最后子集只有一个元素的时候,数组就排好序了。

  2. 伪码描述

    void QuickSort(ListElementType *A,int N)
    {
        pivot = 从A[]中选取一个主元;
        将剩余元素分成两个子集:
        A1 = {a∈A[] | a ≤ pivot}和
        A2 = {a∈A[] | a ≤ pivot};
    
        A[] = QuickSort(A1,Size1) ∪ pivot ∪ QuickSort(A2,Size2);
    }
    
  3. 选取主元
    主元的选取其实关乎算法的复杂度,如果主元每次都选中最小的或最大的一个元素的话,快速算法的时间复杂度其实就会变成 O ( N 2 ) O(N^2) O(N2),而当主元是整个数组的第(Size/2)大的时候(刚好将两个子集长度差最小),快速排序的时间复杂度就会变成 O ( N log ⁡ N ) O(N \log N) O(NlogN)。但是我们并不知道无序数组中的每个元素的大小,所以当我们选取主元的时候,一般会取无序数组第一个元素、中间元素和最后元素三个元素的中位数。

    /* 快速排序中寻找主元的操作,采用取第一个元素、中间元素和最后一个元素的中位数的方法 */
     ListElementType Median3(ListElementType *A, int Left, int Right)
     {
         int Center = (Left + Right) / 2; /* 数组中间位置的元素 */
    
         /* 交换位置,使得A[Left] < A[Center] < A[Right],取A[Center]作为主元 */
         if (A[Left] > A[Center])
         {
             Swap(&A[Left], &A[Center]);
         }
         if (A[Left] > A[Right])
         {
             Swap(&A[Left], &A[Right]);
         }
         if (A[Center] > A[Right])
         {
             Swap(&A[Center], &A[Right]);
         }
    
         /* 交换倒数第二个元素和A[Center],方便后面将数组子集的划分 */
         Swap(&A[Center], &A[Right - 1]);
    
         /* 返回主元 */
         return A[Right-1];
    
     }
    
  4. 子集划分

    • 在选取主元的函数中,我们已经将主元放在了A[Right-1]的位置上,且,已经直到A[Left] ≤ \leq A[Right-1] ≤ \leq A[Right],所以我们只需要处理从A[Left+1]到A[Right-2]的元素。
    • 例:下面的序列是从A[Left+1]到A[Right-1],其中A[Right-1]=6就是数组的主元,对下面的序列进行子集划分。
      例子
      1. 定义左边的指针i和右边的指针j;
        1
      2. i指向的元素与主元6进行比较,如果A[i] < < < 6,i继续向右移动,直到A[i] ≥ \geq 6,此时i暂停;
        2
      3. j指向的元素与主元6进行比较,如果A[j] > > > 6,j就继续向左移动,直到A[j] ≤ \leq 6,此时j暂停;
        3
      4. 此时A[i]指向元素大于等于6,A[j]指向元素小于等于6,将A[i]与A[j]交换位置;
        4
      5. 重复第2、第3和第4步,直到i > > > j;
        5
        6
        7
        8
      6. 此时A[i]是数组中最左边的大于主元6的元素,将A[i]和6互换位置。至此划分子集的操作完成。
        9
        10
  5. 代码实现

      /* 接口不规范的快速排序 */
     void Quick_Sort(ListElementType *A, int Left,int Right)
     {
         int i = 0, j = 0;
         ListElementType Pivot; /* 寻找主元 */
    
         /* 如果数组元素比阈值大,则调用快速排序,否则直接插入排序就好了 */
         if (Left < Right)
         {
             Pivot = Median3(A, Left, Right); /* 找到主元 */
             i = Left ;
             j = Right - 1;
    
             /* 划分子集 */
             while (i < j)
             {
             	i++;
             	j--;
                 while (A[i] < Pivot)
                 {
                     i++;
                 }
                 while (A[j] > Pivot)
                 {
                     j--;
                 }
    
                 if (i < j)
                 {
                     Swap(&A[i], &A[j]);
                 }
                 else
                 {
                     break;
                 }
             }
             Swap(&A[Right - 1], &A[i]);
    
             /* 递归调用快速排序 */
             Quick_Sort(A, Left, i - 1);
             Quick_Sort(A, i + 1, Right);
         }
     }
     /* 快速排序 */
     void QuickSort(ListElementType *A, int Size)
     {
         Quick_Sort(A, 0, Size - 1);
     }
    
  6. 代码优化
    我们都知道使用递归函数是很占用系统资源的,为了加快排序速度,可以设置一个阈值CutOff,当递归某一阶段中数组的元素个数小于了这个阈值CutOff,就不必再继续调用递归函数,而是直接插入排序就好了。

    /* 接口不规范的快速排序 */
    void Quick_Sort(ListElementType *A, int Left,int Right)
    {
        int i = 0, j = 0;
        ListElementType Pivot; /* 寻找主元 */
    
        /* 如果数组元素比阈值大,则调用快速排序,否则直接插入排序就好了 */
        if (CutOff <= Right - Left)
        {
            Pivot = Median3(A, Left, Right); /* 找到主元 */
            i = Left;
            j = Right - 1;
    
            /* 划分子集 */
            while (i < j)
            {
            	i++;
            	j--;
                while (A[i] < Pivot)
                {
                    i++;
                }
                while (A[j] > Pivot)
                {
                    j--;
                }
    
                if (i < j)
                {
                    Swap(&A[i], &A[j]);
                }
                else
                {
                    break;
                }
            }
            Swap(&A[Right - 1], &A[i]);
    
            /* 递归调用快速排序 */
            Quick_Sort(A, Left, i - 1);
            Quick_Sort(A, i + 1, Right);
        }
        else
        {
            InsertionSort(A + Left, Right - Left + 1);
        }
    }
    
    /* 快速排序 */
    void QuickSort(ListElementType *A, int Size)
    {
        Quick_Sort(A, 0, Size - 1);
    }
    
  7. 时间复杂度
    最坏情况时间复杂度: T w o r s t ( N ) = O ( N 2 ) T_{worst}(N)=O(N^2) Tworst(N)=O(N2)
    最好情况时间复杂的: T b e s t ( N ) = O ( N log ⁡ N ) T_{best}(N)=O(N \log N) Tbest(N)=O(NlogN)
    平均时间复杂度: T a v g ( N ) = O ( N log ⁡ N ) T_{avg}(N)=O(N \log N) Tavg(N)=O(NlogN)
    空间复杂度: S ( N ) = O ( log ⁡ 2 N ) S(N)=O(\log_2 N) S(N)=O(log2N)

  8. 稳定性
    快速排序是不稳定排序。

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值