#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,今天先不实现,后面再接着更新