力扣leetcode 349. 两个数组的交集 C语言排序解法

本文详细描述了如何通过堆排序算法对数组进行排序,结合前后指针法去除重复元素,最后找出两个有序数组的交集,以解决特定编程题目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

力扣349题

        对于这题,如果用暴力求解   [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;
}

四、总结

        这是一个比较笨的方法,但我认为还算好理解,排序方法随便用一种就行,再去重,最后处理交集。

        写得不好,敬请指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值