算法初步学习

算法:

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

输入:一个算法有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)
二分查找:
对待查找的数据必须有序,从数据中间开始比较查找
块查找:
是一种数据处理的思想,而不是一种特定的算法,适合数据量非常多时,可以先把数据进行分块处理,然后再进行分块查找

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

//	顺序查找
int order_search(int* arr,int len,int key)
{
	for(int i=0; i<len; i++)
	{
		if(arr[i] == key)
			return i;
	}
	return -1;
}

//排序
void sort(int* arr,int len)
{
	for(int i=0; i<len-1; i++)
	{
		for(int j=i+1; j<len; j++)
		{
			if(arr[j] < arr[i])
			{
				int temp = arr[i];
				arr[i] = arr[j];
				arr[j] = temp;
			}
		}
	}
}

//	循环二分
int bin_search_for(int* arr,int len,int key)
{
	int l = 0, r = len-1;
	while(l <= r)
	{
		int p = (l+r)/2;
		if(arr[p] == key) return p;
		if(key < arr[p])
			r = p-1;
		else
			l = p+1;
	}
	return -1;
}

int _bin_search(int* arr,int l,int r,int key)
{
	if(l > r) return -1;

	int p = (l+r)/2;
	if(arr[p] == key)
		return p;
	if(key < arr[p])
		return _bin_search(arr,l,p-1,key);
	else
		return _bin_search(arr,p+1,r,key);
}

//	递归二分
int bin_search(int* arr,int len,int key)
{
	return _bin_search(arr,0,len-1,key);	
}

int main(int argc,const char* argv[])
{
	int arr[10] = {};
	for(int i=0; i<10; i++)
	{
		arr[i] = rand()%100;	
		printf("%d ",arr[i]);
	}
	printf("\nindex:%d\n",order_search(arr,10,150));
	sort(arr,10);
	for(int i=0; i<10; i++)
	{
		printf("%d ",arr[i]);	
	}
	printf("\nindex:%d\n",bin_search_for(arr,10,93));

}

排序算法:

排序算法的稳定性:

    在待排序的数据中,如果有相同的数据,排序过程中不会改变它们的先后顺序,则认为该排序算法是稳定的 

冒泡:

    数据左右进行比较,把最大的数据交换到最后,特点是该算法对数据的有序性敏感,在排序的过程中发现有序可以立即停止排序。
    如果待排序的数据基本有序,则冒泡排序的效率是非常高的
        时间复杂度: 最好O(n) 平均O(n^2)
        稳定的

选择:

    假定最开始的位置是最小值并记录下标min,然后与后面的数据进行比较,如果有比min为下标的数据小的
    则更新min,最后如果min的位置与开始位置发生了改变,则交换min位置的数据与开始位置的数据,
    以此类推。
    虽然时间复杂度挺高的,但是数据交换的次数比较少,因此实际运行时间并不慢(交换比比较耗时)
        时间复杂度: O(n^2)
        不稳定    10 10 1

插入:

    把数据看作两部分,一部分是有序,把剩余的数据逐个插入进去。
    适合对已经排序后的数据,新增数据后进行排序
        时间复杂度: O(n^2)
        稳定的

希尔:

    是插入排序的增强版,由于插入排序时,数据的移动速度比较慢,所以增加了增量的概念,以此来提高
    排序速度
        时间复杂度:O(n^2)
        不稳定

快速:

    找到一个标杆,一方面从左边找比标杆大的数据,找到后把数据放在标杆右边,另一方面从
    右边找比标杆小的数据,找到后放在标杆的左边,最终标杆左边的数据比右边的小,这次就两部分有序,
    然后用同样的方法排序标杆左边和右边的数据
    特点:综合性最高,因此叫快速排序,笔试面试考得最多的排序算法
    时间复杂度:O(nlogn)
    不稳定

归并:

    把一组数据拆分成单独的个体,然后按照从小到大的顺序复制到临时空间,复制完成后再从临时空间拷贝
    到原内存中。
    特点:由于需要使用额外内存空间来避免数据交换的耗时,是一种典型的以空间换时间的排序算法
    时间复杂度:    O(nlogn)
    稳定的

堆:

    先把数据当做完全二叉树,然后把树调整成大根树,然后把根节点交换到数组最后,然后数量--,
    然后再调整成大根树,以此类推,直到数量为1时结束,可以递归、也可以循环实现
    时间复杂度:O(nlogn)
    不稳定的

计数:

    找出数据中的最大值、最小值,并创建哈希表,把数据-最小值作为哈希表数组的下标来访问哈希表并+1,
    然后遍历哈希表,如果数据大于0时,则把下标+最小值还原回数据,依次放回原数组中,达到排序的目的。
    该排序算法理论上速度是非常快,但是有很大的局限性,适合整型数据,而且数据的差值不宜过大,否则
    会非常浪费内存,也是以空间换时间的排序算法
    时间复杂度:O(n+k)  k是整数的范围
    稳定的

桶:

    把数据根据值来存储到不同的桶,然后再调用其它的排序算法,对桶中的数据进行排序,然后再拷贝回数组
    中,以此达到降低排序规模来减少排序的时间,也是以空间换时间的方式。
    缺点:如何分桶,桶定义多大
    时间复杂度:O(n+k)
    稳定的

基数:

    是桶排序的具体实现,首先创建10个队列,然后逆序计算出数据的个十百千...然后分别压入对应的队列中
    ,然后从队列中弹出存储的数据到数组中。
    缺点:只适合排序正整数,需要准备很大的空间,也是空间换时间
    时间复杂度:O (nlog(r)m),其中r为所采取的基数,而m为堆数
    稳定的
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define LEN 10
#define swap(a,b) {typeof(a) t=(a);(a)=(b);(b)=t;}

void show_arr(int* arr,size_t len)
{
	for(int i=0; i<len; printf("%02d ",arr[i++]));
	printf("\n");
}

typedef void (*SortFP)(int*,size_t);//函数指针,具体指向哪个不知道,但是可以指向所有的排序算法

//	冒泡排序
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;
			}
		}
	}
	show_arr(arr,len);
	printf("%s\n",__func__);
}

//	选择排序
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]);
	}
	show_arr(arr,len);
	printf("%s\n",__func__);
}

//	插入排序
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;
	}
	show_arr(arr,len);
	printf("%s\n",__func__);
}

//	希尔排序 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;
		}
	}
	show_arr(arr,len);
	printf("%s\n",__func__);
}

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);
	show_arr(arr,len);
	printf("%s\n",__func__);
}

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));//把tmp拷贝到arr
}

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);
	show_arr(arr,len);
	printf("%s\n",__func__);
	free(tmp);
}

void create_heap(int* arr,int root,size_t len)
{
	//	root是根节点的下标
	if(root >= len) return;

	//	左右子树下标
	int left = root*2+1;	//(root+1)*2-1,root=0
	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);
	}
	show_arr(arr,len);
	printf("%s\n",__func__);
}

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]);
		}
	}
	show_arr(arr,len);
	printf("%s\n",__func__);
}


//	计数排序
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);
	show_arr(arr,len);
	printf("%s\n",__func__);
}

int main(int argc,const char* argv[])
{
	int arr[LEN] = {};
	SortFP sort[] = {bubble_sort,select_sort,insert_sort,shell_sort,quick_sort,merge_sort,heap_sort,heap_sort_for,count_sort};
	for(int i=0; i<sizeof(sort)/sizeof(sort[0]); i++)
	{
		for(int j=0; j<LEN; j++)
		{
			arr[j] = rand()%100;	
		}
		show_arr(arr,LEN);
		sort[i](arr,LEN);//
	}
}

统计十种排序算法:

    排序    最优    最差    平均    特点    是否稳定    空间复杂度
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值