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

//查找/检索算法: 
//平均比较/查找次数: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)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

步骤:

  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网:描述工程的预计进度的带权有向图,只有一个源点和汇点。从源-》汇的最大路径长度成为关键路径,其上的活动称为关键活动。 
 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anyanyamy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值