力扣刷题——排序

  1. 判断能否形成等差数列

给你一个数字数组 arr
如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false

示例 1:
输入:arr = [3,5,1]
输出:true
解释:对数组重新排序得到 [1,3,5] 或者 [5,3,1]
任意相邻两项的差分别为 2 或 -2 ,可以形成等差数列。

示例 2:
输入:arr = [1,2,4]
输出:false
解释:无法通过重新排序得到等差数列。

思路1数组排序+逐项验证
最直观的思路就是,先把数组排序
然后一次遍历挨个判断两两之间的间距是否相等
如果相等,则为等差,否则不符合条件

int
cmpfun(void const* a, void const* b) {
     
    return ((*(int*)a) - (*(int*)b));
}

bool canMakeArithmeticProgression(int* arr, int arrSize) {
     
    if (2 == arrSize)return true;
    qsort(arr, arrSize, sizeof(int), cmpfun); //快排
    int gap = arr[1] - arr[0];	//记录两个数之间的差值
    for (size_t i = 2; i < arrSize; i++) {
     	//逐项判断
        if (arr[i] - arr[i - 1] != gap)
            return false;
    } return true;
}

女少口啊
思路2验证等差求和公式
我们易知等差数列的求和公式为 (首项+尾项)x项数÷2
而首项和尾项之和自然就是最小项和最大项之和(显而易见)
所以,我们其实不需要对数组进行排序
我们只需要一次遍历数组,得到:最小项、最大项、总和
就可以根据上述求和公式验证是否为等差数列

bool canMakeArithmeticProgression(int* arr, int arrSize) {
     
    double sum = 0.0;
    int max = -99999999, min = +99999999;
    for (size_t i = 0; i < arrSize; sum += arr[i++]) {
     
        if (arr[i] > max)max = arr[i];
        if (arr[i] < min)min = arr[i];
    } return ((min + max) * arrSize / 2.0 == sum);
}

.
.
.

  1. 数组的相对排序

给你两个数组,arr1 和 arr2,
arr2 中的元素各不相同
arr2 中的每个元素都出现在 arr1 中
对 arr1 中的元素进行排序,
使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。
未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

示例:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]

提示:
arr1.length, arr2.length <= 1000
0 <= arr1[i], arr2[i] <= 1000
arr2 中的元素 arr2[i] 各不相同
arr2 中的每个元素 arr2[i] 都出现在 arr1 中

思路桶排序&哈希
这道题的思路源自网友。
首先需要知道桶排序这个东西,如果不知道的话,移步这个网站了解一下:
点击链接进行跳转: 桶排序快速扫盲

对桶排序有了个大概了解之后,这道题大概就有了思路了。
而且,观察题目所给的提示,也容易受到启发从而想到桶排序

具体思路是:
新建一个bucket数组,然后仿造桶排序,
以arr1的元素作为下标,对bucket中相应元素进行递增(有点类似于哈希)
紧接着对arr2进行遍历,
以arr2的元素作为下标,把bucket中符合条件的元素(即在arr1中出现过的元素)的下标取出来赋值到arr1
注意,在我们把arr1的元素“安排”进bucket之后,arr1已经是废物一个了
最后我们再把bucket中剩下的元素(也就是只在arr1中出现的元素)按照桶排序输出到arr1的末尾
Done!

int*
relativeSortArray(int* arr1, int arr1Size, 
                    int* arr2, int arr2Size, 
                    int* returnSize) {
     
    *returnSize = arr1Size;
    int bucket[1001] = {
      0 };   //定义一个桶
    for (size_t i = 0; i < arr1Size; i++) {
     
        bucket[arr1[i]]++;  //用bucket来记录arr1出现的元素
    }

    int loc = 0;            //定义一个变量用来重新存储arr1的元素
    for (size_t i = 0; i < arr2Size; i++) {
     
        //注意这里要用while而不是if
        while (bucket[arr2[i]]) {
         //因为一个数字可能重复出现多次
            arr1[loc++] = arr2[i];  //把arr2存在的值写入arr1
            bucket[arr2[i]]--;      //桶计数值减一
        }
    }

    for (size_t i = 0; i < 1001; i++) {
      //遍历桶把剩下的元素输出
        while (bucket[i]) {
          //符合条件的是只在arr1中出现过的元素
            arr1[loc++] = i;
            bucket[i]--;
        }
    } return arr1;
}

.
.
.

  1. 去掉最低工资和最高工资后的工资平均值

给你一个整数数组 salary ,数组里每个数都是 唯一 的,
其中 salary[i] 是第 i 个员工的工资。
请你返回去掉最低工资和最高工资以后,剩下员工工资的平均值。

示例 1:
输入:salary = [4000,3000,1000,2000]
输出:2500.00000
解释:最低工资和最高工资分别是1000 和 4000 。
去掉最低工资和最高工资以后的平均工资是 (2000+3000)/2= 2500

示例 2:
输入:salary = [1000,2000,3000]
输出:2000.00000
解释:最低工资和最高工资分别是 1000和 3000 。
去掉最低工资和最高工资以后的平均工资是 (2000)/1= 2000

思路不要排序!
这道题为什么会有人想到要去排序?!
直接一次遍历,求最大值、最小值、总和,然后返回需要的值不就行了?

double average(int* salary, int salarySize) {
     
    double sum = 0.0;
    int min = 1000000, max = 1000;
    for (int i = 0; i < salarySize; sum += salary[i++]) {
     
        if (salary[i] > max)max = salary[i];
        if (salary[i] < min)min = salary[i];
    } return (sum - max - min) / (salarySize - 2);
}

.
.
.

  1. 有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true

示例 2:
输入: s = “rat”, t = “car”
输出: false

说明:
你可以假设字符串只包含小写字母。

思路1哈希
这个应该是最容易想到的思路了,把每个字符映射到哈希表里面计数,
然后判断两个哈希表是否相等,不相等就false,全相等就true

bool isAnagram(char* s, char* t) {
     
    int hashs[26] = {
      0 }, hasht[26] = {
      0 };
    for (int i = 0; s[i]; hashs[s[i++] - 'a']++);
    for (int i = 0; t[i]; hasht[t[i++] - 'a']++);
    for (int i = 0; i < 26; i++) {
     
        if (hashs[i] != hasht[i])
            return false;
    } return true;
}

思路2位运算异或——我估计就我这奇葩想出来这种方法了
因为我们要判断两个字符串包含的元素是否相同,
于是可能会想到说,通过位运算的异或操作来排除相同的,
如果两个字符串元素全等,那么异或之后应该为0
(参考题268、题389)

于是有了以下代码:

bool isAnagram(char* s, char* t) {
     
    char ret = 0;
    int sum1 = 0, sum2 = 0;
    for (int i = 0; s[i]; ret ^= s[i++]);
    for (int i = 0; t[i]; ret ^= t[i++]);
    return !ret;
}

但是这段代码只通过了力扣34个用例中的30个,卡在“aa”、“bb”这一对用例
所以我们发现如果两个字符是在本字符串中相消的,就有问题

于是我们就想着说,我们在这个基础上再顺带求一求字符串的和,
然后结尾再判断两个字符串各自之和是否相等
于是有了以下代码:

bool isAnagram(char* s, char* t) {
     
    char ret = 0;
    int sum1 = 0, sum2 = 0;
    for (int i = 0; s[i]; sum1 += s[i], ret ^= s[i++]);
    for (int i = 0; t[i]; sum2 += t[i], ret ^= t[i++]);
    return (!ret) && (sum1 == sum2);
}

这段代码过了33/34个测试用例,卡在"xaaddy"、"xbbccy"这一对用例
所以还是有点遗憾,本来挺新颖的思路的…

.
.
.

  1. 根据数字二进制下 1 的数目排序

给你一个整数数组 arr 。
请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。
如果存在多个数字二进制中 1 的数目相同,
则必须将它们按照数值大小升序排列。
请你返回排序后的数组。

示例 1:
输入:arr = [0,1,2,3,4,5,6,7,8]
输出:[0,1,2,4,8,3,5,6,7]
解释:[0]是唯一一个有 0 个 1 的数。
[1,2,4,8] 都有 1 个 1 。
[3,5,6] 有 2 个 1 。
[7] 有 3 个 1 。
按照 1 的个数排序得到的结果数组为 [0,1,2,4,8,3,5,6,7]

示例 2:
输入:arr = [1024,512,256,128,64,32,16,8,4,2,1]
输出:[1,2,4,8,16,32,64,128,256,512,1024]
解释:数组中所有整数二进制下都只有 1 个 1,
所以你需要按照数值大小将它们排序

思路快排,自己写比较函数
这个是网友的思路。
就,直接快排呗,反正题目要求是要排序,
只不过我们这里排序不单纯只是比较数字大小,
而是根据1的数量来作为排序依据,所以我们自己重新写cmpfun

int hammingWeight(uint32_t n) {
     
    int cnt = 0;
    while (n) {
     
        if (n & 1)cnt++;
        n >>= 1;
    } return cnt;
}

int cmpfun(void const* a, void const* b) {
     
    int ai = *(int*)a;
    int bi = *(int*)b;
    int cnta = hammingWeight(ai);
    int cntb = hammingWeight(bi);
    return (cnta == cntb) ? ai - bi : cnta - cntb;
    //注意这里不是单纯的return cnta-cntb或者ai-bi
    //而是要考虑如果位数相同则按照大小来,否则按照位数来
}


int* sortByBits(int* arr, int arrSize, int* returnSize) {
     
    qsort(arr, arrSize, sizeof(int), cmpfun);
    *returnSize = arrSize;
    return arr;
}

.
.
.

  1. 按奇偶排序数组 II

给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。
对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;
当 A[i] 为偶数时, i 也是偶数。
你可以返回任何满足上述条件的数组作为答案。

示例:
输入:[4,2,5,7]
输出:[4,5,2,7]
解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。

思路1开辟一个新的数组空间,遍历数组进行筛选
新建一个等大的数组,然后对原数组进行遍历,
遇到奇数就放到奇数位,遇到偶数就放到偶数位,然后下标移位2

int* sortArrayByParityII(int* A, int ASize, int* returnSize) {
     
    *returnSize = ASize;
    int even = 0, odd = 1;
    int* retArr = (int*)malloc(sizeof(int) * ASize);
    for (int i = 0; i < ASize; i++) {
     
        if (A[i] % 2 == 0) {
     
            retArr[even] = A[i];
            even += 2;
        } else {
     
            retArr[odd] = A[i];
            odd += 2;
        }
    } return retArr;
}

思路2原地双指针转换
大家应该都会往这个方向想,但是具体实现可能不知道要咋操作
原地双指针的好处是不需要额外开辟空间,很棒

对于本题,奇偶对半,
只要处理好奇数,偶数就自然而然肯定“被处理好了”
同理,只要处理好偶数,奇数也就不用担心了

这里我们只处理偶数位,即A[0]、A[2]、A[4]…
两个下标,分别指向奇数位和偶数位(起始分别为1和0)
遍历数组,每次判断偶数位的数字是否为偶数
如果不是,则:在奇数位找到第一个不为奇数的偶数,二者交换
然后继续,直到遍历完数组

int* sortArrayByParityII(int* A, int ASize, int* returnSize) {
     
    *returnSize = ASize;
    int even = 0, odd = 1;
    for (; even < ASize; even += 2) {
     
        if (A[even] % 2 != 0) {
      //偶数位为奇数
            while (A[odd] % 2 == 1)
                odd += 2;       //在奇数位找偶数

            //交换两个数
            int temp;
            temp = A[even];
            A[even] = A[odd];
            A[odd] = temp;
        }
    } return A;
}

.
.
.

  1. 两个数组的交集 II

给定两个数组,编写一个函数来计算它们的交集

示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]

思路1排序+双指针
先对两个数组分别进行升序排序
然后双指针分别各自指向两个数组
判断两个指针指向的元素是否相等
如果不等,则“指向较小元素的指针”向后移位
如果相等,将结果存储到一个数组,同时两个指针同时向后移位
循环重复上述过程,直到其中某个数组遍历完成

int
cmpfun(void const* a, void const* b) {
     
    return (*(int*)a > * (int*)b) ? 1 : 0;
} //注意这里不能直接返回a-b,因为测试用例其中有一例给了临界值
//如果临界值再减可能就要溢出了,所以这里直接判断大小,返回1或0

int* intersect(int* nums1, int nums1Size,
    int* nums2, int nums2Size, int* returnSize) {
     
    qsort(nums1, nums1Size, sizeof(int), cmpfun);
    qsort(nums2, nums2Size, sizeof(int), cmpfun);

    int locret = 0;
    int* retArr = (int*)calloc((nums1Size > nums2Size ? nums2Size : nums1Size), sizeof(int));	//申请一个长度等于较小数组的新数组用于返回
    for (int loc1 = 0, loc2 = 0; loc1 < nums1Size && loc2 < nums2Size; ) {
     
        if (nums1[loc1] < nums2[loc2]) {
     
            loc1++;
        } else if (nums1[loc1] > nums2[loc2]) {
     
            loc2++;
        } else if (nums1[loc1++] == nums2[loc2++]) {
     	//同时移位
            retArr[locret++] = nums1[loc1 - 1];	//存入相同值
        }
    } *returnSize = locret;
    return retArr;
}

思路2哈希
这道题蛮容易想到哈希映射的,但是由于题目没有明确给出元素大小范围和元素个数范围,C语言不方便哈希…代码就没敲了

.
.
.

  1. 重新排列字符串

给你一个字符串 s 和一个 长度相同 的整数数组 indices 
请你重新排列字符串 s ,其中第 i 个字符需要移动到 indices[i] 指示的位置
返回重新排列后的字符串。

示例 1:
输入:s = “aiohn”, indices = [3,1,4,2,0]
输出:“nihao”

示例 2:
输入:s = “aaiougrt”, indices = [4,0,2,6,7,3,1,5]
输出:“arigatou”

示例 3:
输入:s = “art”, indices = [1,0,2]
输出:“rat”

思路新开辟数组空间+按indices重新写入数据
如题,开辟一个等大的空间,然后依次读入indices的新loc,
再在新数组的相应位置写入数据

char* restoreString(char* s, int* indices, int indicesSize) {
     
    char* retArr = (char*)calloc(indicesSize + 1, sizeof(char));
    for (int i = 0; i < indicesSize; i++) {
     
        retArr[indices[i]] = s[i];
    } return retArr;
}

你可能感兴趣的:(Leetcode)