//查找/检索算法: //平均比较/查找次数: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 20 typedef 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; else return -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; } else low = 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)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。
步骤:
排序效果:
介绍:
归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用
步骤:
排序效果:
介绍:
堆积排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
步骤:
(比较复杂,自己上网查吧)
排序效果:
介绍:
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
排序效果:
介绍:
冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
步骤:
排序效果:
介绍:
插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
步骤:
排序效果:
(暂无)
介绍:
希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位>
排序效果:
#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网:描述工程的预计进度的带权有向图,只有一个源点和汇点。从源-》汇的最大路径长度成为关键路径,其上的活动称为关键活动。