面试题目--一步步优化快速排序

#if 1
void swap(int* a, int i, int j)
{
	int tmp = a[i];
	a[i] = a[j];
	a[j] = tmp;
}
void select_piovt(int *a, int start, int end)
{
	int mid = (start + end)/2;
	
	//end is max
	if(a[end] < a[mid])
	{
		swap(a, mid , end);
	}
	if(a[end] < a[start])
	{
		swap(a, start, end);
	}
	//end is middle big num
	if(a[mid] < a[start])
	{
		swap(a, end, start);
	}
	else
	{
		swap(a, end, mid);
	}
}
int partition(int *a, int start, int end)
{
    int p1 = start -1;
	int p2 = start;
    int pivot = a[end];

    if(end - start > 3)
	{
		select_piovt(a, start, end);
	}

    pivot = a[end];

    
	for(; p2 < end; ++p2)
	{
		if(a[p2] < pivot)
		{
            ++p1;
			swap(a, p1, p2);
		}
	}
	swap(a, end, p1+1);
	return p1+1;
}
void QuickSort(int* a, int start, int end)
{
	int pivot = partition(a, start, end);

	if(pivot-1 > start)
	{
     	QuickSort(a, start, pivot - 1);
	}
    if( pivot+1 < end)
	{
    	QuickSort(a, pivot+1, end);
	}

}


int produce_rand_num(int n,int *a)
{
	int i = 0;
		srand((unsigned)time(NULL));
	for(i =0 ;i < n; ++i)
	{

    	a[i] = rand() % 10000;
    	printf("a[%d] = %d, ",i, a[i]);
	}

	return 0;
}
void printf_num_to_screem(int n , int*a)
{

	int i = 0;
	for(; i < n ;++i)
	{
		printf("%d  ", a[i]);
	}
	printf("\n");
}
int main()
{

    int a[30] = {6,5,88,1,0,3,7,444,98,100,1,2,3,4,5,6,7,8,9,0,  9999,4,4,5,22,3333,4,5,6,4};
	int size = 30;
	int i = 0;

	int b[100000];
//	struct timeval tpstart,tpend; 
//    float timeuse;
	clock_t start = 0,end = 0;


	printf("there are %d nums\n", size);
	QuickSort(a,0,size-1);
	for(i = 0; i < size; ++i)
	{
		printf(" %d  ", a[i]);
	}
	printf("\n");
//
	produce_rand_num(100000, b);

//	gettimeofday(&tpstart,NULL);
	start = clock();
	QuickSort(b , 0, 99999);
//	gettimeofday(&tpend,NULL); 
	end = clock();
//	timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+ 	tpend.tv_usec-tpstart.tv_usec; 
//	timeuse/=1000000; 



	



	printf_num_to_screem(100000, b);
    printf("\n\nUsed Time:%f\n\n",(double)(end - start)/((long)1000 ) ); 
	return 0;
}



#endif

上面是最初的未优化的版本,随机生成了100000个随机数,数字范围是0-100000,运行结果:

  0.038s

 

在上面的基础上,我在排序范围比较小以后,选择插入排序,代码实现如下:

void SelectSort(int* a, int start, int end)
{
	int i = 0,j = 0;
	int tmp = a[start];

	for(i = start +1 ; i < end ; ++i)
	{
		tmp = a[i];
		for(j = i - 1; j>=0 && a[i] < a[j]; --j)
		{
			a[j+1] = a[j];
		}
		a[j+1] = tmp;
	}
}
int partition(int *a, int start, int end)
{
    int p1 = start -1;
	int p2 = start;
    int pivot = a[end];

    if(end - start > 3)
	{
		select_piovt(a, start, end);
	}

    pivot = a[end];

    
	for(; p2 < end; ++p2)
	{
		if(a[p2] < pivot)
		{
            ++p1;
			swap(a, p1, p2);
		}
	}
	swap(a, end, p1+1);
	return p1+1;
}
void QuickSort(int* a, int start, int end)
{
	int pivot = 0;
	
	if(end - start < 10)
	{
		SelectSort(a, start, end);
		return ;
	}
	 pivot = partition(a, start, end);

	if(pivot-1 > start)
	{
     	QuickSort(a, start, pivot - 1);
	}
    if( pivot+1 < end)
	{
    	QuickSort(a, pivot+1, end);
	}

}

运行结果1(10以内插入排序): 还真是提升不少

运行结果2(20以内插入排序),下降不少时间,太NB!!

运行结果3(50以内选择排序),下降并不太多的丫子

好了,现在在排序的过程对重复数据做处理,看会不会有什么明显提升

去重复算法:

在数组选主员拆分成两个子数组以后,对主元左右两边的数组中和主元相同的数组挪到主元左右两边,左边数组中相同的挪到主元左边紧挨着主元,右边数组中和主元相同的数字挪到和主元紧挨着的右边。

具体算法解释可看代码注释move_same_pivot_num 函数

void move_same_pivot_num(int*a, int pivot, int start, int end, int* pi_left, int* pi_right)
{
	int i = 0, j = 0;
	int left_num = 0;
	int right_num = 0;
    int key = a[pivot];
	
	//	printf(" pivot = %d, start = %d, end = %d, \n", pivot, start, end);
	if(NULL == a || NULL == pi_left || NULL == pi_right || start > end || pivot < start || pivot > end)
	{	
		return;
	}
	
	/*  */
	i = start;
	j = pivot -1;
	while(i < j)
	{
		/*从主元左边第一个数字开始寻找和主元不相同的数字,找到后停下*/
		while(i < j && a[j] == key)
		{
            --j;
			++left_num;
			
		}
		/*从数组开始的寻找第一个和主元相同的数字,找到后停下*/
		while(i < j && a[i] != key)
		{
			++i;
		}
		/*将靠近主元且和主元不相同的数字 和 远离主元但和主元相同的数字 交换*/
		if(i < j)
		{
			++left_num;
			swap(a, i, j);
			--j;
			++i;
			/*临界处理:如果前后索引指向同一个数字,即将跳出循环,若这个数字和主元相同,主元数目累加 */
			if(i == j && key == a[i])
			{
                ++left_num;
			}
		}
		else
		{
			break;
		}
		
	}
	/*从主元开始向左边 第一个和主元不相同数字的索引,返回,是下次排序的数组的最右边*/
	*pi_left = pivot - left_num;
	
	/*主元右边数组处理,和左边类似*/
	i = pivot +1;
	j = end;
	while(i < j)
	{
		/*从数组最右边的开始寻找第一个和主元相同的数组,找到后停下 */
		while(i < j && a[j] != key)
		{
			--j;
		}
		/*从主元紧挨着的第一个数字开始寻找第一个和主元不相同的数字,找到后停下 */
		while(i < j && a[i] == key)
		{
            ++i;
			++right_num;
			
		}
		/*靠近主元但和主元不相同的数字  和  远离主元但和主元相同的数字  交换 */
		if(i < j)
		{
			++right_num;
			swap(a, i, j);
			
			--j;
			++i;
			/*临界处理*/
			if(i == j && key == a[i])
			{
                ++right_num;
			}
		}
		else
		{
			break;
		}
	}
	/*从主元开始向右边 第一个和主元不相同数字的索引,返回,是下次排序的右边数组的最右边*/
	*pi_right = pivot + right_num;
}
void QuickSort_dup_data_delete(int* a, int start, int end)
{
	int pivot = 0;
	int pi_left = 0, pi_right = 0;

	if(NULL == a || start >= end)
		return;
	
	if(end - start <=  60)
	{
		SelectSort(a, start, end);
		return ;
	}
	pivot = partition(a, start, end);
	pi_left = pivot - 1;
	pi_right = pivot +1;

	move_same_pivot_num(a, pivot,start, end, &pi_left, &pi_right );
	

	if(pi_left > start)
	{
		QuickSort_dup_data_delete(a, start,pi_left);
	}
    if( pi_right < end)
	{
		QuickSort_dup_data_delete(a, pi_right, end);
	}
	
}

int main()
{
	
    int a[30] = {6,5,88,1,0,3,7,444,98,100,1,2,3,4,5,6,7,8,9,0,  9999,4,4,5,22,3333,4,5,6,4};
	int s[10] = {2,3,4,6,7,8,9,20,21,0};
	int size = 30;
	int i = 0;
	
	int b[100000];
	clock_t start = 0,end = 0;
	
	produce_rand_num(100000, b, 100000);
	start = clock();
	QuickSort_dup_data_delete(b , 0, 99999);
	end = clock();
	
	printf_num_to_screem(100000, b);
    printf("\n\nUsed Time:%f \n\n",(double)(end - start)/((long)1000 ) ); 

		QuickSort_dup_data_delete(a , 0, 29);
		printf_num_to_screem(30,a);

	QuickSort_dup_data_delete(s , 0, 10);
	printf_num_to_screem(10,s);
	return 0;
}

 

测试结果1,和上面相同情况下,100000个数字,数字范围是0-100000,运行时间如下:

是的,没错,变慢了!!!费了好大力气去掉重复,竟然没有变快。

但是,因为数字是0-100000随机的,相对来说重复率没那么高,所以我重新取了一组数据:

还是100000个数字,数字范围是0-100(重复率非常高,一个数字至少有1000个), 得到的运行结果是:

降低了一倍多,所以看数据情况,选择相应的优化方案。

 

还有快排进行非递归版本,但是c语言没有自带的stack库,要自己实现个stack,今天先不实现,后面再接着更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值