数据结构算法总结

分类 :

(一)插入类排序:直接插入排序,折半插入排序, 希尔排序

(二)交换类排序:冒泡排序, 快速排序

(三)选择类排序:简单选择排序,树形选择排序,堆排序

(四)归并排序: 归并排序

(五)分配类排序

二、插入类排序:

(一)   思想:在一个已经排好序的序列中,将未被排进的元素按照原先的规定插入到指定位置。

(二)   分类:

1、  直接插入排序:

①   思想:最基本的插入排序,将第i个插入到前i-1个中的适当位置。

②   时间复杂度:T(n) = O(n²)。

③   空间复杂度:S(n) = O(1)。

④   稳定性:稳定排序。循环条件while(r[0].key < r[j].key)保证的。

⑤   程序:

[cpp] view plaincopy

  1. void InsSort(RecordType r[], int length)
  2. {
  3. for(i = 2; I <= length; i++)
  4. {
  5. r[0] = r[i];
  6. j = i – 1;
  7. while(r[0].key < r[j].key)
  8. {
  9. r[j + 1] = r[j]; j = j – 1;
  10. }
  11. r[j+1] = r[0];
  12. }
  13. }

2、  折半插入排序:

①   思想:因为是已经确定了前部分是有序序列,所以在查找插入位置的时候可以用折半查找的方法进行查找,提高效率。

②   时间复杂度:比较时的时间减为O(n㏒n),但是移动元素的时间耗费未变,所以总是得时间复杂度还是O(n²)。

③   空间复杂度:S(n) = O(1)。

④   稳定性:稳定排序。

⑤   程序:

[cpp] view plaincopy

  1. void BinSort(RecordType r[], int length)
  2. {
  3. for(i = 2; i <= length; i++)
  4. {
  5. x = r[i];
  6. low = 1; high = i – 1;
  7. while(low <= high)
  8. {
  9. mid = (low + high) / 2;
  10. if(x.key < r[mid].key)
  11. high = mid – 1;
  12. else
  13. low = mid – 1;
  14. }
  15. for(j = i – 1; j >= low; --j)
  16. r[j + 1] = r[j];
  17. r[low] = x;
  18. }
  19. }

3、  希尔排序:

①   思想:又称缩小增量排序法。把待排序序列分成若干较小的子序列,然后逐个使用直接插入排序法排序,最后再对一个较为有序的序列进行一次排序,主要是为了减少移动的次数,提高效率。原理应该就是从无序到渐渐有序,要比直接从无序到有序移动的次数会少一些。

②   时间复杂度:O(n的1.5次方)

③   空间复杂度:O(1)

④   稳定性:不稳定排序。{2,4,1,2},2和1一组4和2一组,进行希尔排序,第一个2和最后一个2会发生位置上的变化。

⑤   程序:

[cpp] view plaincopy

  1. void ShellInsert(RecordType r[], int length, int delta)
  2. {
  3. for(i = 1 + delta; i <= length; i++)/*1+delta为第一个子序列的第二个元素的下表*/
  4. if(r[i].key < r[1 - delta].key)
  5. {
  6. r[0] = r[i];
  7. for(j = i – delta; j > 0 && r[0].key < r[j].key; j -=delta)
  8. r[j + delta] = r[j];
  9. r[j + delta] = r[0];
  10. }
  11. }
  12. void ShellSort(RecordType r[], int length, int delta[], int n)
  13. {
  14. for(i = 0; i <= n – 1; ++i)
  15. ShellInsert(r, length, delta[i]);
  16. }

三、交换类排序:

(一)   思想:通过交换逆序元素进行排序的方法。

(二)   分类:

1、  冒泡排序:

①   思想:反复扫描待排序序列,在扫描的过程中顺次比较相邻的两个元素的大小,若逆序就交换位置。第一趟,从第一个数据开始,比较相邻的两个数据,(以升序为例)如果大就交换,得到一个最大数据在末尾;然后进行第二趟,只扫描前n-1个元素,得到次大的放在倒数第二位。以此类推,最后得到升序序列。如果在扫描过程中,发现没有交换,说明已经排好序列,直接终止扫描。所以最多进行n-1趟扫描。

②   时间复杂度:T(n) = O(n²)。

③   空间复杂度:S(n) = O(1)。

④   稳定性:稳定排序。

⑤   程序:

[cpp] view plaincopy

  1. void BubbleSort(RecordType r[], int length)
  2. {
  3. n = length;
  4. change = TRUE;
  5. for(i = 1; i <= n – 1 && change; i++)
  6. {
  7. change = FALSE;
  8. for(j = 1; j <= n – I; ++j)
  9. if(r[j].key > r[j + 1].key)
  10. {
  11. x = r[j];
  12. r[j] = r[j + 1];
  13. r[j + 1] = x;
  14. change = TRUE;
  15. }
  16. }
  17. }

2、  快速排序:

①   思想:冒泡排序一次只能消除一个逆序,为了能一次消除多个逆序,采用快速排序。以一个关键字为轴,从左从右依次与其进行对比,然后交换,第一趟结束后,可以把序列分为两个子序列,然后再分段进行快速排序,达到高效。

②   时间复杂度:平均T(n) = O(n㏒n),最坏O(n²)。

③   空间复杂度:S(n) = O(㏒n)。

④   稳定性:不稳定排序。{3, 2, 2}

⑤   程序:

[cpp] view plaincopy

  1. void QKSort(RecordType r[], int low, int high)
  2. {
  3. int pos;
  4. if(low < high)
  5. {
  6. pos = QKPass(r, low, high);
  7. QKSort(r, low, pos - 1);
  8. QKSort(r, pos + 1, high);
  9. }
  10. }
  11. int QKPass(RecordType r[], int left, int right)
  12. {
  13. RecordType x;
  14. int low, high;
  15. x = r[left];
  16. low = left;
  17. high = right;
  18. while(low < high)
  19. {
  20. while(low < high && r[high].key >= x.key)
  21. high--;
  22. if(low < high)
  23. {
  24. r[low] = r[high];
  25. low++;
  26. }
  27. while(low < high && r[low].key < x.key)
  28. low++;
  29. if(low < high)
  30. {
  31. r[high] = r[low];
  32. high--;
  33. }
  34. }
  35. r[low] = x;
  36. return low;
  37. }

四、选择类排序:

(一)   思想:每一趟在n – i + 1 ( i = 1,2, … , n - 1)个记录中选取关键字最小的记录作为有序序列中的第i个记录。

(二)   分类:

1、  简单选择排序:

①   思想:第一趟时,从第一个记录开始,通过n – 1次关键字的比较,从n个记录中选出关键字最小的记录,并和第一个记录进行交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,直至全部排序完毕。

②   时间复杂度:T(n) = O(n²)。

③   空间复杂度:S(n) = O(1)。

④   稳定性:不稳定排序,{3, 3, 2}。

⑤   程序:

[cpp] view plaincopy

  1. void SelectSort(RecordType r[], int length)
  2. {
  3. n = length;
  4. for(i = 1; i <= n - 1; i++)
  5. {
  6. k = i;
  7. for(j = i + 1; j <= n; i++)
  8. if(r[j].key < r[k],key)
  9. k = j;
  10. if(k != i)
  11. {
  12. x = r[i];
  13. r[i] = r[k];
  14. r[k] = x;
  15. }
  16. }
  17. }

2、  树形选择排序:

①   思想:为了减少比较次数,两两进行比较,得出的较小的值再两两比较,直至得出最小的输出,然后在原来位置上置为∞,再进行比较。直至所有都输出。

②   时间复杂度:T(n) = O(n㏒n)。

③   空间复杂度:较简单选择排序,增加了n-1个额外的存储空间存放中间比较结果,就是树形结构的所有根节点。S(n) = O(n)。

④   稳定性:稳定排序。

⑤   程序:

3、  堆排序:

①   思想:把待排序记录的关键字存放在数组r[1…n]中,将r看成是一刻完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,一下个记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。

②   时间复杂度:T(n) = O(n㏒n)。

③   空间复杂度:S(n) = O(1)。

④   稳定性:不稳定排序。{5, 5, 3}

⑤   程序:

(1)     调整堆:

[cpp] view plaincopy

  1. void sift(RecordType r[], int k, int m)
  2. {
  3. /*假设r[k...m]是以r[k]为根的完全二叉树,而且分别以r[2k]和r[2k+1]为根的左右子树为大根堆,调整r[k],使整个序列r[k...m]满足堆的性质*/
  4. t = r[k];/*暂存“根”记录r[k]*/
  5. x = r[k].key;
  6. i = k;
  7. j = 2 * i;
  8. finished = FALSE;
  9. while(j <= m && !finished)
  10. {
  11. if(j < m && r[j].key < r[j + 1].key)
  12. j = j + 1;/*若存在右子树,且右子树根的关键字大,则沿右分支“筛选”*/
  13. if(x >= r[j].key)
  14. finished = TRUE;/*筛选完毕*/
  15. else
  16. {
  17. r[i] = r[j];
  18. i = j;
  19. j = 2 * i;
  20. }/*继续筛选*/
  21. }
  22. r[i] = t;/*将r[k]填入到恰当的位置*/
  23. }

(2)     建初堆:

[cpp] view plaincopy

  1. void crt_heap(recordType r[], int length)
  2. {
  3. n = length;
  4. for(i = n / 2; i >= 1; --i)/*自第n/2向下取整 个记录开始进行筛选建堆*/
  5. sift(r, i, n);
  6. }

(3)     堆排序:

[cpp] view plaincopy

  1. void HeapSort(RecordType r[], int length)
  2. {
  3. crt_heap(r, length);
  4. n = length;
  5. for(i = n; i >= 2; --i)
  6. {
  7. b = r[1];/*将堆顶记录和堆中的最后一个记录互换*/
  8. r[1] = r[i];
  9. r[i] = b;
  10. sift(r, 1, i - 1);/*进行调整,使r[1…i-1]变成堆*/
  11. }
  12. }

五、归并排序:

(一)   思想:

(二)   分类:

1、  归并排序:

①   思想:假设初始序列右n个记录,首先将这n个记录看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2向上取整 个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。如此重复,直至得到一个长度为n的有序序列为止。

②   时间复杂度:T(n) = O(n㏒n)。

③   空间复杂度:S(n) = O(n)。

④   稳定性:稳定排序。

⑤   程序:

[cpp] view plaincopy

  1. void Merge(RecordType r1[], int low, int mid, int high, RecordType r2[])
  2. {
  3. /*已知r1[low...mid]和r1[mid + 1...high]分别按关键字有序排列,将它们合并成一个有序序列,存放在r2[low...high]*/
  4. i = low;
  5. j = mid + 1;
  6. k = low;
  7. while((i <= mid) && (j <= high))
  8. {
  9. if(r1[i].key <= r1[j].key)
  10. {
  11. r2[k] = r1[i];
  12. ++i;
  13. }
  14. else
  15. {
  16. r2[k] = r1[j];
  17. ++j;
  18. }
  19. ++k;
  20. }
  21. while(i <= mid)
  22. {
  23. r2[k] = r1[i];
  24. k++;
  25. i++;
  26. }
  27. while(j <= high)
  28. {
  29. r2[k] = r1[j];
  30. k++;
  31. j++;
  32. }
  33. }
  34. void MSort(RecordType r1[], int low, int high, RecordType r3[])
  35. {
  36. /*r1[low...high]经过排序后放在r3[low...high]中,r2[low...high]为辅助空间*/
  37. RecordType r2[N];
  38. if(low == high)
  39. r3[low] = r1[low];
  40. else
  41. {
  42. mid = (low + high) / 2;
  43. MSort(r1, low, mid, r2);
  44. MSort(r1, mid + 1, high, r2);
  45. Merge(r2, low, mid, high, r3);
  46. }
  47. }
  48. void MergeSort(RecordType r[], int n)
  49. {
  50. /*对记录数组r[1...n]做归并排序*/
  51. MSort(r, 1, n, r);
  52. }

六、分配类排序:

(一)   思想:分配类排序是利用分配和收集两种基本操作。

(二)   分类:

1、  多关键字排序:

2、  链式基数排序:

①   思想:先分配,再收集,就是先按照一个次关键字收集一下,然后进行收集(第一个排序),然后再换一个关键字把新序列分配一下,然后再收集起来,又完成一次排序,这样所有关键字分配收集完后,就完成了排序。

②   时间复杂度:T(n) = O( d ( n + rd ) )。

③   空间复杂度:S(n) = O(rd)。

④   稳定性:稳定排序。

⑤   程序:

七、总结:

(1)简单排序法一般只用于n较小的情况(例如n<30)。当序列的记录“基本有序”时,直接插入排序是最佳的排序方法。如果记录中的数据较多,则应采用移动次数较少的简单选择排序法。

(2)快速排序、堆排序和归并排序的平均时间复杂度均为O(n㏒n),但实验结果表明,就平均时间性能而言,快速排序是所有排序方法中最好的。遗憾的是,快速排序在最坏情况下的时间性能为O(n²)。堆排序和归并排序的最坏时间复杂度仍为O(n㏒n),当n较大时,归并排序的时间性能优于堆排序,但它所需的辅助空间最多。

(3)可以将简单排序法与性能较好的排序方法结合使用。例如,在快速排序中,当划分子区间的长度小于某值时,可以转而调用直接插入排序法;或者先将待排序序列划分成若干子序列,分别进行直接插入排序,然后再利用归并排序法,将有序子序列合并成一个完整的有序序列。

(4)基数排序的时间复杂度可以写成O(d·n)。因此,它最适合于n值很大而关键字的位数d较小的序列。当d远小于n时,其时间复杂度接近O(n)。

(5)从排序的稳定性上来看,在所有简单排序法中,简单选择排序是不稳定的,其他各种简单排序法都是稳定的。然而,在那些时间性能较好的排序方法中,希尔排序、快速排序、堆排序都是不稳定的,只有归并排序、基数排序是稳定的。

时间: 2024-07-31 09:56:50

数据结构算法总结的相关文章

【数据结构&amp;&amp;算法系列】KMP算法介绍及实现(c++ &amp;&amp; java)

KMP算法如果理解原理的话,其实很简单. KMP算法简介 这里根据自己的理解简单介绍下. KMP算法的名称由三位发明者(Knuth.Morris.Pratt)的首字母组成,又称字符串查找算法. 个人觉得可以理解为最小回溯算法,即匹配失效的时候,尽量少回溯,从而缩短时间复杂度. KMP算法有两个关键的地方,1)求解next数组,2)利用next数组进行最小回溯. 1)求解next数组 next数组的取值只与模式串有关,next数组用于失配时回溯使用. 在简单版本的KMP算法中,每个位置 j 的 n

数据结构算法1------算法和算法分析

最近面试iOS实习生.几次都是败在数据结构算法上面了.决定重新捡起大学的课本,复习一下数据结构算法相关知识. 1.反转一个链表.循环算法.               1     List   reverse(List   l)   {       2     if(!l)   return   l;       3         list   cur   =   l.next;       4     list   pre   =   l;       5     list   tmp;  

初转java随感(一)程序=数据结构+算法

大学刚学编程的时候,有一句很经典的话程序=数据结构+算法 今天有了进一步认识. 场景: 1.当前局面 (1)有现成的封装好的分页组件 返回结果是page.类型为:Page.包括 page 分页信息,data 数据列表 List型. (2)查询了一个数据列表 midResult.类型为 List<Map<String,Object>> . 2.想要的结果 现在想把page midResult 两个结果集进行处理.返回页面. 3.解决问题 首先纠结的是,用哪种类型来存储数据.用List

数据结构——算法设计(12页)方案三

方案三的思路尽可能减少元素的重复挪动,设法实现元素移动一次性地“最终定位”:同时在空间效率方面,避免大段复制数据元素序列,降低缓存元素所需的辅助空间使用量. 代码如下: #include<stdio.h> int gcd(int a , int b) { if(a%b!=0) gcd(b,a%b) ; else return b ; } void leftshift3(int *a , int n , int p) { int i , j , k , m , d ; int temp ; in

数据结构——算法之(012)( linux C 所有字符串操作函数实现)

题目:实现linux C下常用的字符串操作函数 题目分析: 一.面试中可能经常遇到这样的问题:比如strcpy.memcpy.strstr 二.参考了linux 内核代码,对linux大神表示感谢,代码写得相当精致,这里拿来与大家分享吧 算法实现: /* * linux/lib/string.c * * Copyright (C) 1991, 1992 Linus Torvalds */ /* * stupid library routines.. The optimized versions

day40 数据结构-算法(二)

什么是数据结构? 简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中. 比如:列表.集合与字典等都是一种数据结构. N.Wirth: "程序=数据结构+算法" 列表 列表:在其他编程语言中称为"数组",是一种基本的数据结构类型. 关于列表的问题: 列表中元素使如何存储的? 列表提供了哪些基本的操作? 这些操作的时间复杂度是多少? 列表与可变对象* 栈 栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表. 栈的特点:后进先出(las

python入门、python数据分析(numpy、matplotlib、sklearn等)tensflow、爬虫、机器学习、深度学习、自然语言处理、数据挖掘、机器学习项目实战、python全栈、PHP、java、java web、openCV、hadoop、matlab、android、数据结构算法和刷题等教学视频

扫描二维码加好友购买视频,绝对优惠,谢谢支持. python入门和进阶熟练教学视频 入门: 进阶: python数据分析教学视频 python数据分析晋级班 tensorflow教程及实战 python爬虫教学 机器学习课程 深度学习课程 机器学习项目班 自然语言处理教学视频 python全栈教学视频 数据挖掘视频 PHP教学视频 java java web openCV教学视频 Hadoop教学视频 matlab教学 andriod教学视频 数据结构算法班及面试班 原文地址:https://w

数据结构算法——算法复杂度分析

算法复杂度分为时间复杂度和空间复杂度 首先要清楚一点,大O表示法的时间复杂度高不代表程序运行时间长,空间复杂度高不代表占用空间多. 他们表示的是代码执行时间随着数据规模增长的变化趋势.和算法储存空间与数据规模之间的增长关系. 时间复杂度判断方法 1.只关注循环次数最多的一段代码 2.加法法则:总复杂度等于量级最大的那段代码的复杂度 3.乘法原则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积 常见的复杂度量级(按数量级递增) 常量阶:O(1) 对数阶:O(logn) 线性阶:O(n) 线性对数阶:

前端要不要学数据结构&amp;算法

我们都知道前端开发工程师更多偏向 DOM 渲染和 DOM 交互操作,随之 Node 的推广前端工程师也可以完成服务端开发.对于服务端开发而言大家都觉得数据结构和算法是基础,非学不可.所以正在进行 Node 开发的同学而言,这个答案跃然纸上.我们今天重点说一说纯前端开发的同学到底需不要数据结构与算法. 我先说下结论:需要,非常需要. 第一,只要是程序员,基本功都是数据结构与算法 从我们接触编程的时候就知道一个理论,程序=数据结构+算法.所以,只要写的是程序,就离不开数据结构和算法.当然,有的同学会

数据结构算法(1)--栈与队列

数据结构算法(1)--栈与队列 总结并记录学习数据结构过程中遇到的问题及算法. 一些常见算法: Note: 基础应用. 递归的非递归转化. 阶乘 递归实现: #include <iostream> using namespace std; int F(int n) { if (n == 0 || n == 1) return 1; else return n * F(n - 1); } int main() { int s; cin >> s; int result = F(s);