赞
踩
给定线性序集中n个元素和一个整数k 【k=(n+1)/2】,要求找出这n个元素中第k小的元素,即找中位数。线性序列没有排序,没有重复值。
已知快速排序划分时一个划分基准数的位置在确定后,在之后排序中是不会变的。利用此特性,以下算法模仿快速排序,但只对划分出的子数组之一进行处理,时间复杂度为O(n),比排序完查找更快。
如果n=0或1,不需要找(唯一一个数据就是要找的)
如果当前基准数不是所求的,在相应的子串中找(相当于快排中划分好了的三段)
查询中位数将k设为n/2。以下可以查询第k小的任何一个数。
由于使用分治策略,这里的k是在子串中相对的第k小(k是相对left和right的位置),但是划分函数返回的下标mid是绝对位置,所以,可以用一个变量(j)存储mid在子串中的相对位置。
SelectK每一次递归时left和right也会发生变化,相应的k也要注意改变(k-j)。
- int SelectK(int* nums, int left, int right, int k)
- {
- if (left == right&&k==1)//剩下一个元素找里面第一小的元素
- return nums[left];
- int mid = Partition(nums, left, right);
- int j = mid + 1 - left;//j是相对在子串中的位置(第j个)
- if (k <= j)return SelectK(nums, left, mid, k);
- else return SelectK(nums, mid + 1, right, k-j);
- }
- int SelectKMin(int* nums, int n, int k)
- {
- if (nullptr == nums || n < 1 || k<1 || k>n)
- return -1;
- return SelectK(nums, 0, n - 1, k);
- }
划分函数
同快排的划分
- int Partition(int*nums,int left,int right)
- {
- int tmp = nums[left];
- while (left < right)
- {
- while (left<right && nums[right]>tmp)right--;
- if(left<right)nums[left] = nums[right];
- while (left < right && nums[left] < tmp)left++;
- if (left < right)nums[right] = nums[left];
- }
- nums[left] = tmp;
- return left;
- }
测试
- int main()
- {
- int a[] = { 5,3,6,7,9,8,4,2,1,0 };
- int n = sizeof(a) / sizeof(a[0]);
- int k = 0;
- for (k = 1; k <= n; k++)
- {
- int kmin = SelectKMin(a, n, k);
- printf("k:%d kmin:%d\n", k, kmin);
- }
- }
给定一维线上n个点,找其中的一对点,使得在n个点组成的所有点对中,该点对的距离最小。如果有多于一对只找1对作为解。
如果暴力求解每一对之间的距离需要O(n^2)太慢了。将每个点的位置存在数组中,利用分治法:
将数组划分后产生两个子集合,差值最小有三个部位可能产生:
当集合中元素个数<2时,无法计算差值,此时应返回INT_MAX
设左边集合为S1,最小差为d1;右边集合为S2,最小差d2;S2中最小值q和S1最大值p相减得的差,三个数相比最小的值即为最接近点对的差值。
为了将数组平分减少计算次数,需要找中位数并用其作为划分基准。
主要代码:
- int CpairNum(int* nums, int left, int right)
- {
- if ((right - left) < 1)return INT_MAX;//少于两个数没有差值
- int m = (right - left + 1) / 2;//当前数组中位数的位置(left~right)
- int pos = left + m - 1;//绝对位置(0~n-1)
- SelectK(nums, left, right, m);//找中位数的值,然后划分成左右两个数组
- int d1 = CpairNum(nums, left,pos);//S1继续递归
- int d2 = CpairNum(nums,pos+1 ,right );//S2继续递归
- int p = MaxS1(nums, left ,pos);//S1中最大的元素
- int q = MinS2(nums,pos+1, right);//S2中最小的元素
- return Min3(d1, d2, q - p);//对比找到最小元素
- }
- int Cpair(int* nums, int n)
- {
- if (n < 2 || nums == nullptr)return INT_MAX;
- return CpairNum(nums, 0, n - 1);
- }
其余代码:
- //划分函数
- int Partition(int* nums, int left, int right)
- {
- int tmp = nums[left];
- while (left < right)
- {
- while (left<right && nums[right]>tmp)right--;
- if (left < right)nums[left] = nums[right];
- while (left < right && nums[left] < tmp)left++;
- if (left < right)nums[right] = nums[left];
- }
- nums[left] = tmp;
- return left;
- }
- //找第k小元素下标
- int SelectK(int* nums, int left, int right, int k)
- {
- if (left == right && k == 1)
- return nums[left];
- int mid = Partition(nums, left, right);
- int j = mid - left + 1;
- if (k == j)return nums[mid];
- if (k <j)return SelectK(nums, left, mid, k);
- else return SelectK(nums, mid + 1, right, k - j);
- }
- //S1里面最大的
- int MaxS1(int*nums,int left ,int right)
- {
- return nums[left];
- }
- //S2里面最大的
- int MinS2(int *nums,int left,int right)
- {
- int min = INT_MAX;
- for (int i = left; i <= right; i++)
- {
- if (nums[i] < min)
- {
- min = nums[i];
- }
- }
- return min;
- }
- //三个最小差里面最小的
- int Min(int a, int b)
- {
- return a < b ? a : b;
- }
- int Min3(int a, int b, int c)
- {
- return Min(a, Min(b, c));
- }
排好序再计算的方法无法直接推广到二维的情形,而分治法求解可以。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。