数据结构复习-图、查找、排序

来源:互联网 发布:七上生物行知天下答案 编辑:程序博客网 时间:2024/05/17 22:58
//查找/检索算法: //平均比较/查找次数:ASL=pi*ci  i:1~n ci是找第i个记录需要进行比较的次数 //静态查找:顺序查找O(N)、二分查找O(log2 N)、分块查找 //动态查找:二叉排序树、平衡二叉树 //排序算法: 以从小到大为例 //基于选择:选择排序 O(N^2)、堆排序 O(N*log2 N)//基于交换:冒泡排序 O(N^2)、快速排序 O(N*log2 N)//基于比较:插入排序 O(N^2)、希尔排序 //其他:合并排序 O(N*log2 N)、基数排序 O(d(r+N)) -- d最长位数,r基数(即链表的个数),N数字个数 int LinearSearch(int arr[],int item){}int BinarySearch(int arr[],int n,int item){int low = 0,high = n-1,mid;while(low <= high){mid = (low+high)/2;if(arr[mid] == item){return mid;}if(arr[mid] > item){high = mid -1;}else low = mid + 1;}return -1;}//性能介于顺序和二分法之间的,二分查找索引表,块内采取顺序查找 #define MAX 20typedef struct {int key; //关键字int link; //对应块的起始下标 }IdxType;typedef IdxType IDX[MAX];int BlockSearch(IDX I, int m,int r[],int n,int item){int low = 0,high = m-1,mid , i;int b = n/m;   //每块的记录个数while(low <= high){mid = (low + high)/2;if(I[mid].key >= k)high = mid -1;else low = mid +1;} //查找完索引表后,顺序查找块中元素 i = I[high+1].link;while(i <= I[high+1].link + b -1 && r[i].key!=k)i ++;if(i <= I[high +1].link +b-1)return i;elsereturn -1;}//内部查找的: //二叉排序树:BST  左子树<根节点<右子树,按中序遍历得到的是递增序列//任何节点插入二叉排序树时,均以叶子节点的形式插入。对一个序列依次插入所有节点。 //构造: O(N*log2 N)  查找:O(log2 N) int BTreeSearch(int arr[], int item){}//平衡二叉树:AVL  每个节点的平衡因子(左子树高度-右子树高度)为0,-1,1   查找O(log2 N) int BalTreeSearch(int arr[],int item){}//外部查找的://B-(多路平衡查找树) B+树 //哈希表查找//哈希表:H(Ki)是关键字Ki所对应的对象存储在内存中的地址。关键字的取值区间一般远大于哈希地址的变化区间//目标:使哈希地址均匀分布在连续内存上//哈希函数构造方法:直接定址法(h(k) = k+c,浪费空间);除留余数法(h(k) = k mod p,最常使用,p是不大于关键字总数的素数) //哈希冲突(同义词冲突)解决:开放定址法(以冲突的哈希地址为自变量,通过哈希冲突函数得到新的地址),拉链法(把所有的同义词用单链表连接//起来,哈希表中存放链表头指针)//开发定址法:记录规模小时使用,但是不能探查所有冲突,在冲突时做删除标记//拉链法:无法确定表长,需要额外的空间,但是能真正的删除记录  //Sort  //选择排序,始终是O(N^2) void Choose(int arr[],int n){int tmp,k; for (int i = 0; i < n-1 ; i++){k = i;for(int j = i+1; j < n ; j++){if(arr[j] < arr[k]){k = j;}}if(k!= i){tmp = arr[i];arr[j] = arr[k];arr[k] = tmp;}}}  //正序时只需要一遍循环 O(N),平均和最坏都是O(N^2) void Booble(int arr[],int n){int tmp; for (int i = 0; i < n-1 ; i++){for(int j = n-1; j > i ; j--){if(arr[j] < arr[j-1]){tmp = arr[j];arr[j] = arr[j-1];arr[j-1] = tmp;}}}}//假设前i个数已经排好序了,把i+1~n的数插入进去 ////正序时只需要一遍循环 O(N),平均和最坏都是O(N^2)  void Insert(int arr[],int n){int tmp; for(int i = 1;i < n;i++){tmp = arr[i];j = i-1;//找到应该插入的位置 while(j >= 0 && tmp < arr[j]){arr[j+1] = arr[j];j--;}arr[j+1] = tmp;}}//二分法查找插入排序 void BinaInsert(int arr[],int n){int i,j,low=0,high=n-1,mid;int tmp;for(int i = 1;i < n;i++){tmp = arr[i]; //二分查找应该插入的位置 while(low <= high){mid = (low + high)/2;if(tmp < arr[mid]){high = mid-1;} elselow = mid+1;} for(j = i-1; j >= high+1;j--){arr[j+1] = arr[j];}arr[high + 1] = tmp;} }//希尔排序 O(N^1.3) void ShellSort(int arr[],int n){int i,j,gap = n/2,tmp;while(gap>0){for(i = gap; i <n ; i++){//对间隔gap的元素进行直接插入排序 tmp = arr[i];j = i-gap;while(j >= 0 && tmp < arr[j]){arr[j+gap] = arr[j];j = j-gap;}arr[j+gap] = tmp;}//减小增量 gap = gap/2;}} //快速排序: 平均时间:O(N*log2 N) 平均空间:O(log2 N) --- 递归树的高度 //最坏时:正序或者反序所需的比较次数最多: O(N^2)  O(N) void Quick(int arr[],int s,int t){int i = s, j = t;int tmp;if(s < t){tmp = arr[s];while(i!=j){while(j>i && arr[j] > tmp)j --;arr[i] = arr[j];while(i<j && arr[i] < tmp)i ++;arr[j] = arr[i];}arr[i] = tmp;Quick(arr,s,i-1);Quick(arr,i+1,t);}} //堆是完全二叉树,每次选择双亲节点归位,构造初始堆 void sift(int arr[],int low,int high){int i = low,j = 2*i; //j是左孩子 int tmp = arr[i];  //保存双亲节点 while(j <= high){if(j < high && arr[j] < arr[j+1]){j ++;} if(tmp < arr[j]){arr[i] = arr[j];i = j;j = 2*i;}else break;}arr[i] = tmp;} void Heap(int arr[],int n){int i,tmp;//建立初始堆 for(i = n/2;i>=1;i--)sift(arr,i,n);//进行n-1趟堆排序,每次元素个数-1 for(i = n; i >=2; i--){tmp = arr[1];arr[1] = arr[i];arr[i] = tmp;sift(arr,1,i-1);} }//对两个有序表进行二路归并,自底向上的 把表看作多个长度为1的子表进行归并;自顶向下的先分解至子表长度为1,然后进行归并void Merge(int arr[],int low,int mid,int high){int *r;int i = low,j = mid+1,k=0;r = (int *)malloc(sizeof(int)*(high-low +1));while(i <= mid&& j <= high){if(arr[i] <= arr[j]){r[k] = arr[i];i++; k++;}else{r[k] = arr[j];j++; k++;}}while(i<= mid){r[k] = arr[i];i++;k++;}while(j <= high){r[k] = arr[j];j ++; k++;}//把low~mid,mid+1~high归并完后复制回去 for(k = 0,i = low;i<=high;k++,i++) {arr[i] = r[i];}}//O(N*log2 N) void MergeSort(int arr[],int low,int high){int mid;if(low < high){mid = (low + high)/2;MergeSort(arr,low,mid);MergeSort(arr,mid+1,high);Merge(arr,low,mid,high);}}void MergeS(int arr[],int n){MergeSort(arr,0,n-1);}//分配:把元素插入对应的队列中  收集:把队列连成线性表  重复多遍 void Jishu(int arr[],int n){}

查找:http://blog.csdn.net/jiuqiyuliang/article/details/24405965

八大排序:http://blog.csdn.net/jiuqiyuliang/article/details/25304009



==============================================================================================================================

以下转载:视觉感受7种常用排序算法:


1. 快速排序

介绍:

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

步骤:

  1. 从数列中挑出一个元素,称为 “基准”(pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

排序效果:

视觉直观感受7种常用排序算法

 

2. 归并排序

介绍:

归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用

步骤:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针达到序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

排序效果:

视觉直观感受7种常用排序算法

 

3. 堆排序

介绍:

堆积排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

步骤:

(比较复杂,自己上网查吧)

排序效果:

视觉直观感受7种常用排序算法

 

4. 选择排序

介绍:

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。

排序效果:

视觉直观感受7种常用排序算法

 

5. 冒泡排序

介绍:

冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

步骤:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

排序效果:

视觉直观感受7种常用排序算法

 

6. 插入排序

介绍:

插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

步骤:

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

排序效果:

(暂无)

 

7. 希尔排序

介绍:

希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率

2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位>

排序效果:

视觉直观感受7种常用排序算法


#include <stdio.h>#include <stdlib.h>//图的基本概念://有向图:入度,出度//无向图:度//图中所有顶点的度之和=边数*2//完全图:每两个顶点间都有一条边,//路径:从一个顶点到另一个顶点的顶点序列; 路径长度:路径上经过的边的数目//无向图:任意两个顶点间都存在边,极大连通子图=连通分量 //无向图:任意两个顶点间都存在边,极大强连通图=强连通分量//关节点:在删除该节点及其关联的边后,把图的一个连通分量分隔成多个连通分量; 重连通图:没有关节点的连通图//网:即带权图//简单路径/回路:路径上的顶点不重复  //图的邻接矩阵和邻接表存储方法#define MAX 20/*邻接矩阵的特性:1.无向图的邻接矩阵是对称矩阵,可以只存三角矩阵2.不带权的有向图的邻接矩阵是稀疏矩阵,可以用三元组标识3.无向图:邻接矩阵的第i行非0元素的个数是第i个顶点的度(对于有向图,是出度)4.易于确定顶点间是否有边,但是确定总边数时,要按行和列进行扫描,复杂。 */ typedef struct {int no;  //定点编号 char* info; //顶点其他信息 } VertexType;  //顶点类型typedef struct {int edges[MAX][MAX];   //邻接矩阵的边数组 int n,e;  //顶点数,边数 VertexType vxs[MAX];//存放顶点信息 } MGraph;//顺序分配与链式分配的结合,邻接表 /*邻接表:1.表示不唯一2.n,e的无向图需要存储n个顶点和2*e个边,对于稀疏图,邻接表更省空间3.第i个链表的长度是顶点i的度。 4.有向图中只存储了以顶点为起点的边,不宜找到指向该顶点的边,可以设计逆邻接表,保存指向该顶点的边。 */ typedef struct ANode{int adjvex;///该边的终点位置 struct ANode* nextarc;//指向下一条边的指针 char* info;//边的信息 } ArcNode;typedef struct VNode{char* data;         //顶点信息 ArcNode* firstarc;  //指向第一条边 }VNode;typedef VNode AdjList[MAX];typedef struct {AdjList adjlist;  //邻接表 int n,e; //顶点数,边数} ALGraph; /*邻接矩阵-->邻接表: O(N*N)找出表中不为0的元素,然后为该点建立一个表节点并在邻接表对应得单链表中插入。   邻接表-->邻接矩阵: O(N+2E) 在邻接表中找相邻节点,找到后修改矩阵对应值。   N---顶点数   E---边数 */ //图的遍历:BFS  DFS ,只有连通图的情况下才会完全访问//应用:求简单路径和简单回路,可以采用深度优先的回溯方法。 //DFS:从初始顶点触发,对相邻的未被访问过的顶点进行访问,再从该顶点出发进行访问。 //邻接表:O(N+2E)    邻接矩阵:O(N*N) void DFS(ALGraph* G,int v){ArcNode* p;//访问数组,从v开始访问 visited[v] = 1;printf("%d ",v);p = G->adjlist[v].firstarc;while(p!=NULL){if(visited[p->adjvex] == 0){DFS(G,p->adjvex);}p = p->nextarc;}} //BFS:层次遍历,广度搜索void BFS(ALGraph* G,int v){ArcNode* p;//循环队列 int queue[MAX],front = 0,rear = 0;int visited[MAX];int w,i;for(i = 0;i<G->n;i++){visited[i] = 0;}printf("%2d ",v);visited[v] = 1;//v进队 rear = (rear+1)%MAX;queue[rear] = v;//队列不空则循环 while(front!=rear){//出队一个元素 front = (front+1)%MAX;w = queue[front];p = G->adjlist[w].firstarc;while(p!=NULL){//若该顶点未被访问,则访问该顶点,然后进队 if(visited[p->adjvex] == 0){printf("%2d ",p->adjvex);visited[p->adjvex] = 1;rear = (rear+1)%MAX;queue[rear] = p ->adjvex;}p = p->nextarc;}}printf("\n");} // 对于非连通图,需要对于每个顶点进行一次DFS/BFS//生成树:极小连通子图,含有全部顶点,但是只有N-1条边。图中所有生成树中具有边上权值之和最小的为最小生成树。//只能用已有的边来构造: Prim / Criskaer //Prim:初始化U={v},以v到其他顶点的边为候选边,从候选边中选出最小权值的,其对应的顶点加入U中,然后修改候选边,若到新顶点的权值更小则更新其权值//O(N*N)   N---顶点数  两重FOR循环//Criskaer:把G中的边按权重从小到大排列,每次选择最小权值的边,若存在两个顶点所在的连通分量相同,若加入,则形成了回路,舍弃刚刚选取的边 //O(E*E)//改进后O(E*log2 E):可以采用堆排序对边进行排序,利用并查集进行顶点合并,判断是否在一个连通分量。//最短路径:无权图:经过顶点最少的路径; 有权图:经过边的权值之和最小的路径。//求一个顶点到其他各顶点的最短路径: Dijkstra/Floyd//Dijkstra:初始S={v},U中是其他顶点,选取顶点k使其到v距离最小,然后加入S中,可以借助k修改其他顶点到v的最短距离值,再重复选边,加点,改距离值。//O(N*N)   可以利用该方法求最小生成树。 //Floyd: 求每对顶点间的最短路径//产生一个递推矩阵,A(k)[i][j] 表示从i到j经过顶点编号不大于k的最短路径长度。//用Path数组保存i到j中间节点编号不大于k的最短路径长度的前一个节点编号,-1标识没有中间节点 //A(-1)[i][j] = cost[i][j] ;  A(k+1)[i][j] = min{A(k)[i][j],A(k)[i][k+1] + A(k)[k+1][j]}//有向图的拓扑排序://1.选择一个没有前驱的顶点,并输出//2.删去该点,删去从此出发的有向边,重复直到不存在没有前驱的顶点为止。//AOE网:描述工程的预计进度的带权有向图,只有一个源点和汇点。从源-》汇的最大路径长度成为关键路径,其上的活动称为关键活动。  



0 0
原创粉丝点击