当前位置: 首页 > wzjs >正文

济南网站建设 齐鲁文化创意基地网络推广seo怎么弄

济南网站建设 齐鲁文化创意基地,网络推广seo怎么弄,办公室装修设计怎么设计,东莞网页设计文章目录 排序与查找算法1. 排序1.1 如何评估一个排序算法1.2 选择排序代码实现分析 1.3 冒泡排序代码实现分析 1.4 插入排序代码实现分析 1.5 希尔排序代码实现分析 1.6 归并排序代码实现分析 1.7 快速排序代码实现分析改进策略 1.8 堆排序代码实现分析 2. 查找2.1 二分查找代…

文章目录

  • 排序与查找算法
    • 1. 排序
      • 1.1 如何评估一个排序算法
      • 1.2 选择排序
        • 代码实现
        • 分析
      • 1.3 冒泡排序
        • 代码实现
        • 分析
      • 1.4 插入排序
        • 代码实现
        • 分析
      • 1.5 希尔排序
        • 代码实现
        • 分析
      • 1.6 归并排序
        • 代码实现
        • 分析
      • 1.7 快速排序
        • 代码实现
        • 分析
        • 改进策略
      • 1.8 堆排序
        • 代码实现
        • 分析
    • 2. 查找
      • 2.1 二分查找
        • 代码实现
        • 二分查找的变种

排序与查找算法

排序的前提是"比较",排序的目的往往是"查找"。这一章,我们主要给大家介绍一些经典的基于比较的排序算法,以及经常被人低估的二分查找算法。

1. 排序

1.1 如何评估一个排序算法

我们可以从三个维度去分析一个排序算法:时间复杂度、空间复杂度和稳定性。

  1. 时间复杂度

    • 最好情况
    • 最坏情况
    • 平均情况
    • 系数和低阶项
  2. 空间复杂度

    我们需要重点关注原地排序(store in place),也就是空间复杂度为 O(1) 的排序。

  3. 稳定性

    数据集中"相等"的元素,如果排序前和排序后的相对次序不变,那么这个排序算法就是稳定的。稳定性是排序算法一个很重要的指标。

    我们通过一个例子来说明这个问题:在电商系统中,经常需要查看订单数据,以做数据统计。现在有这样一个需求,先对订单按金额从大到小排序,如果金额相同,再按下单时间从大到小排序。 解决思路:先按下单时间从大到小排序,再用稳定的排序算法,按金额从大到小排序。(why?)

1.2 选择排序

选择排序(Selection sort)是一种简单直观的排序算法。

其基本思想是:首先在未排序的数列中找到最小(或最大)元素,然后将其存放到数列的起始位置;接着,再从剩余未排序的元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

以数列[20,40,30,10,60,50]为例,其选择排序过程如下:

在这里插入图片描述

代码实现
// main.c
#include <stdio.h>
#define SIZE(arr) (sizeof(arr) / sizeof(arr[0]))void selection_sort(int arr[], int n);int main(void) {int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};// 选择排序selection_sort(arr, SIZE(arr));printf("排序后数组顺序为:");for (int i = 0; i < SIZE(arr); i++) {printf(" %d", arr[i]);}return 0;
}
// selection_sort.c
void selection_sort(int arr[], int n) {// 从小到大int min = arr[0];int minIdx = 0;for (int i = 0; i < n - 1; i++) {min = arr[i];minIdx = i;for (int j = i + 1; j < n; j++) {// 找出minif (min > arr[j]) {min = arr[j];minIdx = j;}}// 交换int temp = arr[i];arr[i] = arr[minIdx];arr[minIdx] = temp;}
}
分析
  1. 时间复杂度:O(n^2)
  2. 空间复杂度:O(1)
  3. 稳定性:稳定

1.3 冒泡排序

冒泡排序(Bubble sort)也是一种简单直观的排序算法。

其基本思想是:从左到右,依次比较相邻的两个元素,如果它们逆序,则交换这两个元素。每比较一轮(冒泡一次),最大的元素就会"沉"到数列的末尾,而较小的元素会慢慢"浮"到数列的前面。因此,经过 N-1 (假设有N个元素) 次冒泡,数组就排好序了。

以数列 [3, 6, 4, 2, 11, 10, 5] 为例,其冒泡排序过程如下:

在这里插入图片描述

代码实现
void bubble_sort(int arr[], int n) {for (int i = n - 1; i > 0; i--) {for (int j = 0; j < i; j++) {if (arr[j] > arr[j+1]) {// 交换int temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}
分析
  1. 时间复杂度:O(n^2)
  2. 空间复杂度:O(1)
  3. 稳定性:稳定

1.4 插入排序

插入排序(Insertion sort)也是一种简单直观的排序算法。

其基本思想是:构建有序序列。对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

插入排序类似于摸牌并将其从小到大排列。每次摸到一张牌后,根据其点数插入到确切位置。

以数列 [3, 6, 4, 2 ,11, 10, 5] 为例,插入排序过程如下:

在这里插入图片描述

代码实现
// 自己的代码思路 循环和选择结构嵌套得比较深,代码可读性变差
void insert_sort(int arr[], int n) {for (int i = 1; i < n; i++) {int insertNum = arr[i];for (int j = i - 1; j >= 0; j--) {// 找到合适的插入位置if (insertNum >= arr[j]) {arr[j + 1] = insertNum;break;} else {arr[j + 1] = arr[j]; // 比待插入数大的数往后移一位if (j == 0) {arr[j] = insertNum; // 待插入数是最小的情况}}}}
}
// 参考代码
void insertion_sort(int arr[], int n) {for (int i = 1; i < n; i++) { // i:待插入元素的索引// 保存待插入的元素int value = arr[i];int j = i - 1;while (j >= 0 && arr[j] > value) {arr[j + 1] = arr[j]; // 逻辑上的交换操作j--;}// j == -1 || arr[j] <= valuearr[j + 1] = value;}
}
分析
  1. 时间复杂度:O(n^2)
  2. 空间复杂度:O(1)
  3. 稳定性:稳定

1.5 希尔排序

希尔排序(Shell sort)是插入排序的一种改进版本,它是基于插入排序下面两点性质而提出的改进方法:

  • 插入排序在对几乎已经排好序的数据排序时,效率很高,可以达到线性排序的效率。
  • 但插入排序每次只能将数据移动一位,因此一般情况下比较低效。

希尔排序,又叫缩小增量排序。它通过将元素在组间交换,从而可以将小的元素快速地移动到数列的前面。前面几次组间插入排序的目的,是使数列基本有序。最后一次,增量(gap)为 1,也就是简单的插入排序了。

希尔排序的效率与 gap 序列相关,希尔本人推荐的 gap 序列为:n/2, n/4, …, 1。但这个序列并不是最佳的。

以数列 [9, 1, 2, 5, 7, 4, 8, 6, 3, 5] 为例,希尔排序过程如下:

在这里插入图片描述

代码实现
void shell_sort(int arr[], int n) {int gap = n / 2;while (gap != 0) {for (int i = gap; i < n; i++) {int value = arr[i];int j = i - gap;while (j >= 0 && value < arr[j]) {arr[j + gap] = arr[j];j -= gap;} // j < 0 || value >= arr[j]arr[j + gap] = value;}gap /= 2;}
}
分析
  1. 时间复杂度:O(n^2),一般来说小于O(n^2)
  2. 空间复杂度:O(1)
  3. 稳定性:不稳定。因为会长距离的交换元素,因此希尔排序是不稳定的。一般来说,希尔排序的性能优于简单的插入排序,但它牺牲了稳定性。

1.6 归并排序

归并排序(Merge sort)是建立在归并操作上的一种高效的排序算法。该算法是分治思想的一种典型应用。

归并(merge)操作:是指将两个已经有序的序列合并成一个有序序列的操作。

以数列 [38, 27, 43, 3, 9, 82, 10] 为例,归并排序过程如下:

在这里插入图片描述

代码实现
// 归并排序
#define N 10
int tempArr[N];void merge(int arr[], int left, int mid, int right) {int i = left, j = left, k = mid + 1;while (j <= mid && k <= right) {if (arr[j] <= arr[k]) {tempArr[i++] = arr[j++];} else {tempArr[i++] = arr[k++];}}// j > mid || k > rightwhile (j <= mid) {tempArr[i++] = arr[j++];}while (k <= right) {tempArr[i++] = arr[k++];}// 复制数组for (int idx = left; idx <= right; idx++) {arr[idx] = tempArr[idx];}
}void merge_sort_helper(int arr[], int left, int right) { // 递归分解数组// 递归终止条件if (left >= right)return;int mid = left + ((right - left) >> 1);// 将未排序的大数组分为左右子数组merge_sort_helper(arr, left, mid); // 左merge_sort_helper(arr, mid + 1, right); // 右// 将左右子数组合并为有序大数组merge(arr, left, mid, right);
}void merge_sort(int arr[], int n) {merge_sort_helper(arr, 0, n - 1);
}
分析
  1. 时间复杂度 O(nlogn)

    T(n) = 2T(n/2) + O(n)

    如下图所示,每一层的所用的时间为 cn (归并操作所用的时间,其中 c 为常数项),总共有 log2n 层。

在这里插入图片描述

  1. 空间复杂度:O(n)

    所用空间:tmp数组需要O(n)的空间,栈的深度为O(lgn)。因此总的空间复杂度为 O(n) + O(lgn) = O(n)。

  2. 稳定性:稳定

    当左边区间最前面的元素和右边区间最前面的元素相等时,我们放入的是左边区间的元素,因此归并排序是稳定的。

1.7 快速排序

快速排序(Quick sort)是建立在分区操作上的一种高效的排序算法。快速排序也是分治思想的一种典型应用。

快速排序的步骤如下:

  1. 挑选基准值:从数列中挑选一个元素作为"基准值"。
  2. 分区:将比基准值小的元素排列在数列的前端,将比基准值大的元素排列到数列的尾端,与基准值相等的元素可以放到任意一段。分区操作之后,基准值就已经就位了。
  3. 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

分区算法有多种,其中一种如下图所示(基准值为 5):

在这里插入图片描述

代码实现
#include <stdlib.h>
#include <time.h>// 快速排序
void SWAP(int arr[], int idx1, int idx2) {int temp = arr[idx1];arr[idx1] = arr[idx2];arr[idx2] = temp;
}int partition(int arr[], int left, int right) {// 选择基准值srand(time(NULL));int idx = rand() % (right - left + 1) + left;int pivot = arr[idx];SWAP(arr, idx, right); // 将基准值放到最右边// 分区int storgeIdx = left;for (int i = left; i < right; i++) {if (arr[i] < pivot) {SWAP(arr, i, storgeIdx);storgeIdx++;}}SWAP(arr, storgeIdx, right); // 将位于最右边的基准值换回数组中间return storgeIdx;
}void quick_sort_helper(int arr[], int left, int right) {if (left >= right) return;int mid = partition(arr, left, right);quick_sort_helper(arr, left, mid - 1);quick_sort_helper(arr, mid + 1, right);
}void quick_sort(int arr[], int n) {quick_sort_helper(arr, 0, n - 1);
}
分析
  1. 时间复杂度:
    • 最好情况:每次分区都可以分为大小相等的两份。O(nlogn) T(n) = 2T(n/2) + O(n) 分析方法和归并排序一致,这里不再赘述。
    • 最坏情况:每次分区,基准值最终都位于数列的两端。O(n^2)
  2. 空间复杂度:O(logn) 平均情况下,栈的深度为 O(logn)。
  3. 稳定性:在分区操作中,选取基准值后,我们会将其和最右边的元素进行交换。这可能是一次长距离的交换,因此快速排序是不稳定的。
改进策略

快速排序算法虽然最坏情况下的时间复杂度是 O(n2),但是平均情况下时间复杂度是 O(nlogn)。不仅如此,快速排序退化到O(n2)的概率极小,我们还通过合理地选择基准值来避免这种情况发生。

  1. 选取基准值
    • 随机选取
    • 选择 3 个或 5 个元素,然后选取其中位数作为基准值。
  2. 当元素个数比较少时(比如小于等于32个元素),采用插入排序。
  3. 每次分区分为三个区域:pivot。当相等的元素很多时,这种分区方法可以极大地提高快速排序的效率。
  4. 。。。

1.8 堆排序

参考文章:堆排序算法C++实现(以及子节点下标是父节点两倍的证明) - 知乎

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。

堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的 key 值总是小于或大于它的父结点的 key 值。

堆排序算法的步骤如下:

  1. 构建大顶堆

    找到第一个非叶子结点,从后往前构建大顶堆。

  2. 将堆顶元素和无序区的最后一个元素交换,并将无序区的长度减 1。

  3. 将无序区重新调整成大顶堆。

  4. 重复步骤2和步骤3,直到无序区的长度为 1。

堆通常是用一维数组实现的,父子结点的索引满足如下关系(证明见本节参考文章):

lchild(i) = 2i + 1
rchild(i) = 2i + 2
parent(i) = (i - 1)/2
代码实现
// 堆排序
void heapify(int arr[], int i, int n) {while (i < n) {// 如果某个结点下标为i 那么它的左子结点下标为 2i+1 右子节点下标为2i+2int lchild = 2 * i + 1;int rchild = 2 * i + 2;int maxIdx = i; // 该结点与子节点中数值最大的节点的下标if (lchild < n && arr[lchild] > arr[maxIdx]) {maxIdx = lchild;}if (rchild < n && arr[rchild] > arr[maxIdx]) {maxIdx = rchild;}// 到这里,maxIdx是结点i与子结点中最大结点的下标if (i == maxIdx) break; // 该结点的大顶堆构建完成// 运行到这里,说明需要移动元素才能构大顶堆SWAP(arr, i, maxIdx); // 堆顶元素与最大的元素交换i = maxIdx; // 交换后,往下遍历检查子结点的堆是否符合大顶堆结构}
}static void build_heap(int arr[], int n) {// 按照堆底到堆顶的顺序,找到第一个非叶子结点// 第一个非叶子结点的下标 i = (n - 2) / 2// 从该结点开始往前遍历到根节点,建立大顶堆for (int i = (n - 2) >> 1; i >= 0; i--) {heapify(arr, i, n);}
}void heap_sort(int arr[], int n) {// step1 先建立大顶堆build_heap(arr, n);// step2 根据大顶堆构建有序数组// 大顶堆直接对应的数组并不是有序的,但可以确定大顶堆的堆顶元素一定是最大的// ① 所以构建大顶堆后,将根节点(最大元素)与最后一个结点交换,这就得到了有序数组最末尾的元素,② 然后调整堆(除了最后的结点)的结构使其重新变为大顶堆。// 重复步骤一和步骤二,直至有序数组构建完成int len = n; // 无序数组的长度while (len > 1) {SWAP(arr, 0, len - 1);len--;heapify(arr, 0, len);}
}
分析
  1. 时间复杂度:O(nlogn)

    建堆操作的时间复杂度为O(n)(why? 见下图), heapify 操作的时间复杂度为O(logn),循环的次数为 n-1。 T(n) = O(n) + (n-1)*O(logn) = O(nlogn)

在这里插入图片描述

  1. 空间复杂度:O(1)

  2. 稳定性:不稳定

    父子结点交换元素,可能会导致长距离的交换。

2. 查找

2.1 二分查找

二分查找(Binary search),也称为折半查找,是一种在有序数组中查找某一特定元素的算法。

二分查找的步骤如下:

  1. 查找过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则查找过程结束。
  2. 如果查找的元素小于中间元素,则在左半区间查找;否则在右半区间查找。
  3. 这种查找算法每一次比较都会使区间缩小一半,直至找到元素,或者区间为空。

很显然,二分查找的时间复杂度为 O(logn)。

代码实现
// 二分查找(循环实现)
int binary_search(int arr[], int n, int key) {int left = 0, right = n - 1;int mid = 0;while (left <= right) {mid = left + ((right - left) >> 1);if (arr[mid] == key) {return mid;}else if (arr[mid] < key) {left = mid + 1;}else {right = mid - 1;}}return -1; // 没找到
}

二分查找一般都用循环的方式实现。写二分查找时注意以下几点,就能轻松写出 bug free 的代码。

  1. 条件表达式是 left <= right

    left = right 时,说明区间内还有一个元素,该元素也要和 key 值进行比较。

  2. 计算 mid 时,注意避免发生溢出。

    mid = (left + right) / 2 可能会发生溢出,应该写成 mid = left + ((right - left) >> 1)

  3. left 和 right 的更新。

注意不能写成 left = mid ; right = mid ; ,这样可能会导致无限循环

(why? while 循环的条件left <= right 永远满足,永远不会退出循环。如果数组中没有目标元素,那么就会陷入无限循环)

应该写成 left = mid + 1; right = mid - 1; ,中间元素已经和 key 值比较过了。

二分查找的变种
  1. 查找第一个与 key 相等的元素
  2. 查找最后一个与 key 相等的元素
  3. 查找第一个大于等于 key 值的元素
  4. 查找最后一个小于等于 key 值的元素

应用:IP归属地查询

假设有 1000 万条这样的数据,给定一个 IP 地址,如何快速地找到它的归属地?

http://www.dtcms.com/wzjs/187952.html

相关文章:

  • CSS3网站开发盐城seo培训
  • 中信建设招聘长沙seo网站排名
  • 外包网站价格热点军事新闻
  • 东营网站建设哪家好怎么找百度客服
  • 官方微网站加强服务保障满足群众急需i
  • 济南网站建站模板google推广教程
  • 惠州开发做商城网站建设哪家好搜索关键词排名一般按照什么收费
  • wordpress唱片公司模板杭州网络排名优化
  • 网页的网站导航怎么做下载百度安装到桌面
  • 免费做外贸网站深圳网络营销软件
  • 做网站商城互联网公司英文网站seo
  • 做阿里巴巴网站需要多少钱品牌营销策略有哪些
  • 怎么做公司内网网站北京搜索排名优化
  • net后缀的可以做网站吗互联网推广的好处
  • 做网站教程免费网络营销考试题目及答案2022
  • 天津网站推广恢复正常百度
  • 网站搜索引擎优化方案网推公司
  • 做电梯销售从哪些网站获取信息电子商务推广
  • 江门网站优化排名网络营销推广外包服务
  • 怎样推广广告湖南长沙seo
  • 龙泉市建设局门户网站seo公司后付费
  • 成都响应式网站建设公司网奇seo赚钱培训
  • drupal做虚拟发货网站百度搜索引擎怎么弄
  • 新干做网站站长工具端口查询
  • 建设公司网站计入哪个科目移动优化课主讲:夫唯老师
  • 小学生做网站软件苏州seo招聘
  • 网站栏目做跳转福建seo顾问
  • 如何判断网站做的关键词搜索数据
  • bootstrap 风格网站电商网站规划
  • 东莞网站建设17商丘网站优化公司