对于这题,如果用暴力求解 [1,2,2,1] 和 [2,2] 输出的是 [2,2],会发生重复现象,所以我的思路是先去重,但是对于一个既无序又不知数字范围的数组,也很难处理,所以我想首先对数组进行排序,再进行去重操作,最后再对比返回他们的交集数组(代码放在最后)。
一、排序
有很多排序方法,常见的有插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序等。因为题目的要求是不考虑输出结果的顺序,因此选择任意一种排序方法都可以,这里我选择了时间复杂度为O(N*logN)的堆排序。
堆排序的核心是建堆,例如一个数组 arr[] = {8,6,10,5,3,7,4,9,13};把他放在二叉树中就类似于这样
数组的索引依然没有变,只是我们把他画成了上图,这里我们选择构建大堆,就是把最大的值放到最上面,父亲结点的左右子树的值比他小。又把左右子树当父亲节点,他的子节点的依然要比他小。如下图就是一个构建好的大堆。
父节点比他的两个子节点要大 如下图图 13>9 13>10
若该子节点还有子节点,那么他也比他的子节点要大 如图 9>8 9>3
以此类推
如果我们构建出了这样一个堆,就可以把第一个元素a[0]和最后一个元素互换a[size-1],变成了下图这样,这样我们保证了最后一个元素是最大的。因此我们才选择的是大堆
并且在这里我们可以发现,最后一个元素不看的情况下,只有第一个元素不符合大堆,其他元素都符合大堆的特性,因此现在的调整很简单,只需要把左右子节点的较大值与之交换,再往下交换,直到没有子节点 或者 他比自己的子节点大就行了,如下两图。
到这里又可以将最大值与倒数第二个数交换了,以此类推,又调整,在交换,一步一步变成升序。
那到现在我们的首要目标是先构建一个大堆,核心代码如下:
a为指针 表示的是数组首元素的地址 n为数组长度 root为这个节点的索引
代码唯一难点就是child的计算,因为该树是一个完全二叉树
完全二叉树子节点的索引为父亲结点*2+1
//a为指针 表示的是数组首元素的地址 n为数组长度 root为这个节点的索引
void AdjustDown(int* a, int n, int root)
{
int parent = root; //把这个节点设置为父亲节点
int child = parent*2+1;//完全二叉树的左子节点为父亲节点*2+1
while(child<n)
{
if(child+1<n&&a[child]<a[child+1])//判断是否有右子节点,并且比较左右子节点的大小
{
child++; //哪个子节点大 就用那个子节点
}
if(a[child]>a[parent])
{
swap(&a[child],&a[parent]);//子节点比父亲大,就交换,让大的往上走
}
parent = child;
child = parent*2+1; //父亲节点变子节点,子节点又往下走
}
}
很显然这里的代码是从上往下走,如果大堆已经构建好,执行起来就没有问题,如果大堆还未构建,则需要从最后一个非叶子节点 即下图中的5依次往前走,走到10,再到6,再到8。依次步步构建。
代码如下
void Heapsort(int *nums,int numsSize)
{
for(int i=(numsSize-1-1)/2;i>=0;i--)//i为最后一个节点的父节点(即最后一个非叶子节点)
{
AdjustDown(nums,numsSize,i);//从后往前走,保证自己为父节点时是大堆,往上走依然是大堆
}
int end = numsSize-1;//到这已经构建好一个完美的大堆,下面准备开始交换
while(end>0)
{
swap(&nums[0],&nums[end]);//第一个节点和未排序的最后一个节点交换
AdjustDown(nums,end,0);//再次调整,最后的节点已经是最大 无需调整了
end--;
}
}
至此,我们排序函数已经完成了。
把nums1和nums2都排一下序。
Heapsort(nums1,nums1Size);
Heapsort(nums2,nums2Size);
二、去重
到后面我们就该去重了,一个有序的数组去重很简单。我使用的方法是前后指针,第一个元素不用处理,他肯定没有重复,从第二个元素开始如果和begin相等,end就++,如果不等,就先覆盖数组的第二个开始后续的元素,同时刚好也计算了去重后的个数,再从该元素开始又往后续找,详情看代码
int begin = 0;
int count1 = 1;//去重后的个数
int end = begin+1;
//处理nums1的重复
while(end!=nums1Size)//end走到最后再结束
{
if(nums1[begin]!=nums1[end])
{
nums1[count1] = nums1[end];
count1++;
begin = end;
}
end++;
}
对nums2同样做去重处理
begin = 0;
end = begin + 1;
int count2 = 1;
//处理nums2的重复
while(end!=nums2Size)
{
if(nums2[begin]!=nums2[end])
{
nums2[count2] = nums2[end];
count2++;
begin = end;
}
end++;
}
三、处理交集
到最后就可以进行对比了
两个无重复的有序数组的交集,nums1[left]小,就left++往后面找,nums[right]小,就right++往后面找,相等就给到新开辟的空间里,再用计数器计数,在一起往后走,一直找到left或者right到末尾。
int i =0;
int left=0,right = 0;
int count =0;
//left为nums1的位置 right为nums2的位置
int small = count1<count2?count1:count2;
int *tmp = (int*)malloc(sizeof(int)*small);//开辟较小值的空间
while(left<count1&&right<count2)
{
if(nums1[left]>nums2[right])
{
right++;
}
else if(nums1[left]<nums2[right])
{
left++;
}
//相等就放在tmp里面
else
{
tmp[count] = nums1[left];
count++;
left++;
right++;
}
}
*returnSize = count;
return tmp;
至此我们程序就结束了。总代码如下:
void swap(int* a,int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//向下调整排大堆
void AdjustDown(int* a, int n, int root)
{
int parent = root;
int child = parent*2+1;
while(child<n)
{
if(child+1<n&&a[child]<a[child+1])
{
child++;
}
if(a[child]>a[parent])
{
swap(&a[child],&a[parent]);
}
parent = child;
child = parent*2+1;
}
}
//把大堆放在后面 排序完成
void Heapsort(int *nums,int numsSize)
{
for(int i=(numsSize-1-1)/2;i>=0;i--)
{
AdjustDown(nums,numsSize,i);
}
int end = numsSize-1;
while(end>0)
{
swap(&nums[0],&nums[end]);
AdjustDown(nums,end,0);
end--;
}
}
int* intersection(int* nums1, int nums1Size, int* nums2,
int nums2Size, int* returnSize){
Heapsort(nums1,nums1Size);
Heapsort(nums2,nums2Size);
int begin = 0;
int count1 = 1;
int end = begin+1;
//处理nums1的重复
while(end!=nums1Size)
{
if(nums1[begin]!=nums1[end])
{
nums1[count1] = nums1[end];
count1++;
begin = end;
}
end++;
}
begin = 0;
end = begin + 1;
int count2 = 1;
//处理nums2的重复
while(end!=nums2Size)
{
if(nums2[begin]!=nums2[end])
{
nums2[count2] = nums2[end];
count2++;
begin = end;
}
end++;
}
int i =0;
int left=0,right = 0;
int count =0;
//left为nums1的位置 right为nums2的位置
int small = count1<count2?count1:count2;
int *tmp = (int*)malloc(sizeof(int)*small);
while(left<count1&&right<count2)
{
if(nums1[left]>nums2[right])
{
right++;
}
else if(nums1[left]<nums2[right])
{
left++;
}
//相等就放在tmp里面
else
{
tmp[count] = nums1[left];
count++;
left++;
right++;
}
}
*returnSize = count;
return tmp;
}
四、总结
这是一个比较笨的方法,但我认为还算好理解,排序方法随便用一种就行,再去重,最后处理交集。
写得不好,敬请指正!