数据结构算法

算法:

数据结构中的算法,指的是数据结构所具备的功能
解决特定问题的方法,它是前辈们的一些优秀的经验总结

输入:一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算
		法本身定出了初始条件;
输出:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算
		法是毫无意义的;
确定性:算法中的每一条指令必须有确切的含义,不能产生多义性:
可行性:算法中的每一条指令必须是切实可执行的,即原则上可以通过已经实现的基
		本运算执行有限次来实现(也称之为有效性);
有穷性:算法必须能在有限步后终止;
时间复杂度:
由于计算机的性能不同,无法准确地计算出算法执行所需要的时间
因此我们用算法执行的次数来代表算法的时间复杂度 O(公式) 一般忽略常量
    
    常见的时间复杂度:
    //  O(1)
        printf("%d");
    //  O(logn)
    for(int i=n; i>=0; i=i/2)
    {
        printf("%d");
    }
    //  O(n)
    for(int i=0; i<n; i++)
    {
        printf("%d");
    }
    // O(nlogn) 
    for(int i=0; i<n; i++)
    {
        for(int j=n; j>=0; j/=2)
        {
            printf("d");
        }
    }
    //  O(n^2)
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<n; j++)
        {
            printf("%d");
        }
    }
排序算法:
排序算法的稳定性:
    在待排序的数据中,如果有相同的数据,排序过程中不会改变它们的先
    后顺序,则认为该排序算法是稳定的 
冒泡:
    数据左右进行比较,把最大的数据交换到最后,特点是该算法对数据的有序性
    	敏感,在排序的过程中发现有序可以立即停止排序。
    如果待排序的数据基本有序,则冒泡排序的效率是非常高的
        时间复杂度: 最好O(n) 平均O(n^2)
        稳定的
//	冒泡排序
void bubble_sort(int* arr,size_t len)
{
	//	标志位判断排序是否已经完成
	bool flag = true;
	for(int i=len-1; i>0 && flag; i--)
	{
		flag = false;
		for(int j=0; j<i; j++)
		{
			if(arr[j] > arr[j+1])	
			{
				swap(arr[j],arr[j+1]);
				flag = true;
			}
		}
	}
}
选择:
    假定最开始的位置是最小值并记录下标min,然后与后面的数据进行比较,
    如果有比min为下标的数据小的则更新min,最后如果min的位置与开始位置
    发生了改变,则交换min位置的数据与开始位置的数据,以此类推。
    虽然时间复杂度挺高的,但是数据交换的次数比较少,因此实际运行时间
    并不慢(交换比比较耗时)
        时间复杂度: O(n^2)
        不稳定   1 10 10
//	选择排序
void select_sort(int* arr,size_t len)
{
	for(int i=0; i<len-1; i++)
	{
		int min = i;
		for(int j=i+1; j<len; j++)
		{
			if(arr[j] < arr[min]) min = j;	
		}
		if(min != i) swap(arr[i],arr[min]);
	}
}
插入:
    把数据看作两部分,一部分是有序,把剩余的数据逐个插入进去。
    适合对已经排序后的数据,新增数据后进行排序
        时间复杂度: O(n^2)
        稳定的
    //	插入排序
void insert_sort(int* arr,size_t len)
{
	for(int i=1,j; i<len; i++)
	{
		int val = arr[i];
		for(j=i; j>0 && arr[j-1] > val; j--)
		{
			arr[j] = arr[j-1];
		}
		if(j != i) arr[j] = val;
	}
}
希尔:
    是插入排序的增强版,由于插入排序时,数据的移动速度比较慢,所以增加了
    增量的概念,以此来提高排序速度
        时间复杂度:O(n^2)
        不稳定
    //	希尔排序 0  1 2   3 4 5
void shell_sort(int* arr,size_t len)
{
	for(int k=len/2; k>0; k/=2)
	{
		for(int i=k,j; i<len; i++)
		{
			int val = arr[i];
			for(j=i; j-k>=0 && arr[j-k]>val; j-=k)
			{
				arr[j] = arr[j-k];	
			}
			if(j != i) arr[j] = val;
		}
	}
}
快速:
    找到一个标杆,一方面从左边找比标杆大的数据,找到后把数据放在标杆右边,
    另一方面从右边找比标杆小的数据,找到后放在标杆的左边,最终标杆左边的数
    据比右边的小,这次就两部分有序,然后用同样的方法排序标杆左边和右边的数
    据
    特点:综合性最高,因此叫快速排序,笔试面试考得最多的排序算法
    时间复杂度:O(nlogn)
    不稳定
void _quick_sort(int* arr,int left,int right)
{
	if(left >= right) return;

	//	计算标杆的下标
	int pi = (left+right)/2;
	//	备份标杆的数据
	int pv = arr[pi];
	//	备份左右下标
	int l = left,r = right;

	for(int i=left; i<=right; i++)
	{
		if(i==pi) printf("[%d] ",arr[i]);
		else printf("%d ",arr[i]);
	}
	printf("\n");

	while(l < r)
	{
		//	在标杆左边找比它大的数据
		while(l < pi && arr[l] <= pv)  l++;
		if(l < pi)
		{
			arr[pi] = arr[l];
			pi = l;
		}
		//	在标杆右边找比它小的数据
		while(r > pi && arr[r] >= pv) r--;
		if(r > pi)
		{
			arr[pi] = arr[r];
			pi = r;
		}
	}
	//	还原标杆的数据
	arr[pi] = pv;
//	show_arr(arr,LEN);
	if(pi-left>1)	 _quick_sort(arr,left,pi-1);
	if(right-pi>1) 	 _quick_sort(arr,pi+1,right);

}

//	快速排序
void quick_sort(int* arr,size_t len)
{
	_quick_sort(arr,0,len-1);
}
归并:
    把一组数据拆分成单独的个体,然后按照从小到大的顺序复制到临时空间,
    复制完成后再从临时空间拷贝到原内存中。
    特点:由于需要使用额外内存空间来避免数据交换的耗时,是一种典型的
    以空间换时间的排序算法
    时间复杂度:    O(nlogn)
    稳定的
void merge(int* arr,int* tmp,int l,int p,int r)
{
	//	l是左部分最小,p是左部分最大    p+1是右部分最小,r是右部分最大
	//	左右部分各自有序
	if(arr[p] < arr[p+1]) return;

	int i=l,j=p+1,k=l;
	while(i<=p && j<=r)
	{
		//	左右部分从左开始比较,谁小谁放入tmp
		if(arr[i] < arr[j])
			tmp[k++] = arr[i++];
		else
			tmp[k++] = arr[j++];
	}
	//	其中一部分比较完成,另一部分全部放入tmp末尾
	while(i<=p) tmp[k++] = arr[i++];
	while(j<=r) tmp[k++] = arr[j++];
	
	memcpy(arr+l,tmp+l,4*(r-l+1));
}

void _merge_sort(int*arr,int* tmp,int l,int r)
{
	//	拆分
	if(l >= r) return;
	int p = (l+r)/2;
	_merge_sort(arr,tmp,l,p);
	_merge_sort(arr,tmp,p+1,r);
	//	合并
	merge(arr,tmp,l,p,r); //l~p  p+1~r

}

//	归并排序
void merge_sort(int* arr,size_t len)
{
	//	申请临时空间
	int* tmp = malloc(4*len);
	_merge_sort(arr,tmp,0,len-1);
	free(tmp);
}
堆:
    先把数据当做完全二叉树,然后把树调整成大根树,然后把根节点交换到数组
    最后,然后数量--,然后再调整成大根树,以此类推,直到数量为1时结束,
    可以递归、也可以循环实现
    时间复杂度:O(nlogn)
    不稳定的
 void create_heap(int* arr,int root,size_t len)
{
	//	root是根节点的下标
	if(root >= len) return;

	//	左右子树下标
	int left = root*2+1;	//(root+1)*2-1
	int right = root*2+2;	//(root+1)*2+1-1
	//	确保左右子树为左右部分的最大值
	create_heap(arr,left,len);
	create_heap(arr,right,len);
	//	右子树存在,并且右子树大于左子树,则交换
	if(right < len && arr[right] > arr[left])
		swap(arr[right],arr[left]);
	//	左子树存在,并且左子树大于根,则交换
	if(left < len && arr[left] > arr[root])
		swap(arr[left],arr[root]);
}

//	堆排序
void heap_sort(int* arr,size_t len)
{
	//	调整成大顶堆
	create_heap(arr,0,len);
	for(int i=len-1; i>0; i--)
	{
		//	交换根节点与末尾节点
		swap(arr[0],arr[i]);	
		//	重新调整成大顶堆
		create_heap(arr,0,i);
	}
}

void heap_sort_for(int* arr,size_t len)
{
	//	从下往上调整成大顶堆
	for(int i=len-1; i>0; i--)
	{
		int p = (i+1)/2-1;
		if(arr[p] < arr[i])
			swap(arr[p],arr[i]);
	}

	for(int i=len-1; i>0; i--)
	{
		swap(arr[0],arr[i]);
		//	重新调整
		for(int j=0; j<i; j++)
		{
			if(j*2+2 < i && arr[j*2+2] > arr[j*2+1])
				swap(arr[j*2+2],arr[j*2+1]);
			if(j*2+1 < i && arr[j*2+1] > arr[j])
				swap(arr[j*2+1],arr[j]);
		}
	}
}
计数:
    找出数据中的最大值、最小值,并创建哈希表,把数据-最小值作为哈希表数组
    的下标来访问哈希表并+1,然后遍历哈希表,如果数据大于0时,则把下标+最
    小值还原回数据,依次放回原数组中,达到排序的目的。
    该排序算法理论上速度是非常快,但是有很大的局限性,适合整型数据,而且
    数据的差值不宜过大,否则会非常浪费内存,也是以空间换时间的排序算法
    时间复杂度:O(n+k)  k是整数的范围
    稳定的
 //	计数排序
void count_sort(int* arr,size_t len)
{
	int min = arr[0],max = arr[len-1];
	for(int i=0; i<len; i++)
	{
		if(arr[i] > max) max = arr[i];
		if(arr[i] < min) min = arr[i];
	}
	// 创建哈希表
	int* tmp = calloc(4,max-min+1);
	for(int i=0; i<len; i++)
	{
		tmp[arr[i]-min]++;
	}
	for(int i=0,j=0; i<max-min+1; i++)
	{
		while(tmp[i]--) 
		{
			arr[j++] = i+min;	
		}
	}
	free(tmp);
}
桶:
    把数据根据值来存储到不同的桶,然后再调用其它的排序算法,对桶中的数据
    进行排序,然后再拷贝回数组中,以此达到降低排序规模来减少排序的时间,
    也是以空间换时间的方式。
    缺点:如何分桶,桶定义多大
    时间复杂度:O(n+k)
    稳定的
基数:
    是桶排序的具体实现,首先创建10个队列,然后逆序计算出数据的个十百千...
    然后分别压入对应的队列中,然后从队列中弹出存储的数据到数组中。
    缺点:只适合排序正整数,需要准备很大的空间,也是空间换时间
    时间复杂度:O (nlog(r)m),其中r为所采取的基数,而m为堆数
    稳定的
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值