文章目录
序号1
题目:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
解析:采用暴力法
有负数 不好hash 且要记录出现和下标
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
int* ans = (int*)malloc(sizeof(int) * 2);
*returnSize = 0;
for (int i = 0; i < numsSize; i++)
{
for (int j = i + 1; j < numsSize; j++)
{
if (nums[j] == target - nums[i])
{
ans[0] = i;
ans[1] = j;
*returnSize = 2;
return ans;
}
}
}
return ans;
}
序号26
题目:给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
解析:删除元素->重写数组
- 使用快慢指针
慢指针指向重写的下标 快指针指向遍历的下标
int removeDuplicates(int* nums, int numsSize){
if (numsSize <= 1)
return numsSize;
int i = 0;
for (int j = 1; j <numsSize; j++)
{
if (nums[j] != nums[i])
{
i++;
nums[i] = nums[j];
}
}
return i+1;
}
序号27
题目:给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
解析:删除元素->重写数组
- 使用快慢指针
慢指针指向重写的下标 快指针指向遍历的下标 - 26号题是排序,此题无序找特定元素,判定条件不同而已
int removeElement(int* nums, int numsSize, int val){
if (numsSize == 0 )
return 0;
int i = 0;
for (int j = 0; j < numsSize; j++)
{
if (nums[j] != val)
{
nums[i] = nums[j];
i++;
}
}
return i;
}
序号35
题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。假设数组中无重复元素。
解析:
- 查找元素
排序数组->二分法 最后的left就是该插入的位置
int searchInsert(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize - 1;
int mid;
while (left <= right)
{
mid = (left + right) / 2;
if (nums[mid] == target)
return mid;
else if (nums[mid] > target)
right = mid - 1;
else
left = mid + 1;
}
return left;
}
序号53
题目:给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
解析:
- 子数组可以用滑动窗口法
- 这里使用动态规划
- 单变量的动态规划也就是在线处理
sum 记录当前的和,比 0 小,重新计和,比 0 大,就继续加当前值
用一个 ans 记录最大值
int maxSubArray(int* nums, int numsSize){
int sum;
int ans;
sum = nums[0];
ans = nums[0];
for (int i = 1; i < numsSize; i++)
{
//sum = Max(sum, 0) + nums[i]; 下面的if..else..等同意思
if (sum > 0)
sum += nums[i];
else
sum = nums[i];
if (sum > ans)
ans = sum;
}
return ans;
}
序号66
题目:给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
解析:
- 进位
检测当前位置,没有进位直接退出,进位也只进1
如果首位还进位,如 99->100,重新开辟空间,首位置1
int* plusOne(int* digits, int digitsSize, int* returnSize){
for (int i = digitsSize - 1; i >= 0; i--)
{
digits[i] += 1;
digits[i] %= 10;
if (digits[i] != 0)
{
*returnSize = digitsSize;
return digits;
}
}
int* ans = (int*)calloc(digitsSize + 1, sizeof(int));
*returnSize = digitsSize + 1;
ans[0] = 1;
return ans;
}
扩展:如果不是加 1,可用 carry 记录进位数,初始为加数。
最后用 malloc 申请后,再用 memcpy 赋值到 ans+1,ans 首位置1
序号88
题目:给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
解析:
- 写 nums1 数组
因为 nums1 有足够空间,所以从尾开始写,大的放后面
这样不用动 nums1 开始的元素
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int pm = m-1;
int pn = n-1;
int p = nums1Size-1; //nums1尾部下标
while(pm >=0 && pn >= 0)
{
nums1[p] = nums1[pm] > nums2[pn] ? nums1[pm--] : nums2[pn--]; //可用if..else..
p--;
}
if (pn >= 0) //nums2赋值完应该是-1
memcpy(nums1,nums2, sizeof(int) * (pn+1));
}
序号118
题目:给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
解析:
- 参数说明
传指针,有时是改变数组本身,有时是改变指针所指向的变量
*returnSize
传进来为了改变returnSize
**returnColumnSizes
传进来为了改变*returnColumnSizes
- 模拟填数组
每行第一个写 1,末位写 1,中间的ans[i][j] = ans[i-1][j-1] + ans[i-1][j];
int** generate(int numRows, int* returnSize, int** returnColumnSizes) {
int i, index;
*returnColumnSizes = malloc(numRows * sizeof(int)); //*returnColumnSizes存放每行的列数
for (i = 0; i < numRows; i++)
(*returnColumnSizes)[i] = i + 1;
int** res = malloc(numRows * sizeof(int*));
for (i = 0; i < numRows; i++) {
res[i] = malloc((*returnColumnSizes)[i] * sizeof(int));
res[i][0] = 1;
res[i][(*returnColumnSizes)[i] - 1] = 1;
if (i > 1)
{
for (index = 1; index < (*returnColumnSizes)[i] - 1; index++)
res[i][index] = res[i - 1][index - 1] + res[i - 1][index];
}
}
*returnSize = numRows;
return res;
}
序号119
题目:给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
解析:
- 第一种方法就是 118 号题做法,不过很浪费空间
- 第二种运用数学知识,第 k 行的数为
C
k
0
C
k
1
C
k
2
.
.
.
C
k
k
C^0_k \quad C^1_k \quad C^2_k \quad...\quad C^k_k
Ck0Ck1Ck2...Ckk
C n k = n ! / ( k ! ⋅ ( n − k ) ! ) = n ⋅ ( n − 1 ) ⋯ ( n − k + 1 ) / k ! C^k_n = n! / (k! \cdot (n-k)!) = n \cdot (n-1) \cdots (n-k+1)/k! Cnk=n!/(k!⋅(n−k)!)=n⋅(n−1)⋯(n−k+1)/k!
进一步
C n k = C n k − 1 ⋅ ( n − k + 1 ) / k C^k_n = C^{k-1}_n \cdot (n-k+1)/k Cnk=Cnk−1⋅(n−k+1)/k - 在计算组合数时,pre 进行乘法时可能会越界,所以定义为
long long
乘法计算的汇编为:
如 d = a ∗ b / c d = a * b / c d=a∗b/cmov eax,dword ptr [a] imul eax,dword ptr [b] cdq idiv eax,dword ptr [c] mov dword ptr [d],eax
int* getRow(int rowIndex, int* returnSize){
int len = rowIndex + 1;
int* ans = (int*)malloc(sizeof(int) * len);
ans[0] = 1;
long long pre = 1;
for (int i = 1; i < len; i++)
{
pre = pre * (len - i) / i ;
ans[i] = (int)pre;
}
*returnSize = len;
return ans;
}
注:源自于题解
序号121
题目:给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
解析:
- 第一种方法,记录买入的最小价格,卖出的最大价格,输出利润
- 第二种方法运用一点动态规划思路,记录买入的最小价格,更新最大利润。
int maxProfit(int* prices, int pricesSize){
if (pricesSize == 0)
return 0;
int min = prices[0];
int ans = 0;
for (int i = 1; i < pricesSize; i++)
{
if (prices[i] < min)
min = prices[i];
else if (prices[i] - min > ans)
ans = prices[i] - min;
}
return ans;
}
序号122
题目:给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
解析:因为尽可能完成更多交易。
- 保证得到所有的正利润
int maxProfit(int* prices, int pricesSize){
int ans = 0;
for (int i = 1; i < pricesSize; i++)
{
if (prices[i] > prices[i-1])
ans += prices[i] - prices[i-1];
}
return ans;
}
序号167
题目:给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
解析:加数如果大了,就减小;如果小了,就增大。
- 双指针法
left 指向最小值,right 指向最大值,如果大了right--
,如果小了left++
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
int left = 0;
int right = numbersSize - 1;
int* ans = (int*)malloc(sizeof(int) * 2);
int sum;
*returnSize = 0;
while (left < right)
{
sum = numbers[left] + numbers[right];
if (sum == target)
{
ans[0] = left + 1;
ans[1] = right + 1;
*returnSize = 2;
break;
}
else if (sum < target)
left++;
else
right--;
}
return ans;
}
序号169
题目:给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
假设数组是非空的,并且给定的数组总是存在多数元素。
解析:
- 第一种:排序,统计次数
- 第二种,hash 表
- 第三种:投票算法
非众数对众数一换一的抵消,最后还是会剩下众数
int majorityElement(int* nums, int numsSize){
int ans;
int cnt = 0;
for (int i = 0; i < numsSize; i++)
{
if (cnt == 0)
ans = nums[i];
cnt += (nums[i] == ans) ? 1 : -1;
}
return ans;
}
序号189
题目:给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
解析:
- 反转区间代替移动
反转 [0, len - 1 - k],[len - k, len - 1],[0, len - 1] - 保证 k 的范围小于 numsSize
k = k % numsSize;
void Reverse(int* arr, int left, int right)
{
while (left < right)
{
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++;
right--;
}
}
void rotate(int* nums, int numsSize, int k){
k = k % numsSize;
Reverse(nums,0,numsSize-k-1);
Reverse(nums,numsSize-k,numsSize-1);
Reverse(nums,0,numsSize-1);
}
序号217
题目:给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
解析:
- 第一种 hash 表
考虑到可能会有负数,不好用数组 - 第二种排序
排序后,重复的元素挨在一起的
采用堆排序,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
void DownAdjust(int* arr, int len, int pos)
{
int temp = arr[pos];
int child = pos * 2 + 1;
while (child < len)
{
if (child + 1 < len && arr[child] < arr[child + 1])
child++;
if (arr[child] > temp)
{
arr[pos] = arr[child];
pos = child;
}
else
break;
child = pos * 2 + 1;
}
arr[pos] = temp;
}
void HeapSort(int* arr, int len)
{
for (int i = len/2 - 1; i >= 0; i--)
DownAdjust(arr,len,i);
for (int i = len-1; i > 0; i--)
{
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
DownAdjust(arr, i, 0);
}
}
bool containsDuplicate(int* nums, int numsSize){
if (numsSize < 2)
return false;
HeapSort(nums,numsSize);
for (int i = 1; i < numsSize; i++)
{
if (nums[i] == nums[i-1])
return true;
}
return false;
}
序号219
题目:给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。
解析:
- 第一种可以使用 hash ,不过可能存在负数,用数组不方便
- 第二种使用排序
构造结构体,存储下标和值。
对值进行排序
遍历,值相同的判断下标差
typedef struct node
{
int index;
int data;
}node;
void DownAdjust(node* arr, int len, int pos)
{
node temp = arr[pos];
int child = pos * 2 + 1;
while (child < len)
{
if (child + 1 < len && arr[child].data < arr[child+1].data)
child++;
if (arr[child].data > temp.data)
{
arr[pos] = arr[child];
pos = child;
}
else
break;
child = pos * 2 + 1;
}
arr[pos] = temp;
}
void HeapSort(node* arr, int len)
{
for (int i = len/2 - 1; i >= 0; i--)
DownAdjust(arr, len, i);
for (int i = len - 1; i > 0; i--)
{
node temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
DownAdjust(arr, i, 0);
}
}
bool containsNearbyDuplicate(int* nums, int numsSize, int k){
if (numsSize < 2)
return false;
node* newNums = (node*)malloc(sizeof(node) * numsSize);
for (int i = 0; i < numsSize; i++)
{
newNums[i].index = i;
newNums[i].data = nums[i];
}
HeapSort(newNums, numsSize);
for (int i = 1; i < numsSize; i++)
{C
if (newNums[i].data == newNums[i-1].data)
{
if (abs(newNums[i].index - newNums[i-1].index) <= k)
return true;
}
}
free(newNums); //需要手动释放
return false;
}
序号268
题目:给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
解析:
- 第一种方法 hash
遍历一遍序列,记录出现。再遍历 hash 数组 - 第二种使用排序
排完序,检查下标和数是否对应
int missingNumber(int* nums, int numsSize){
int* hash = (int*)calloc(numsSize + 1, sizeof(int));
for (int i = 0; i < numsSize; i++)
hash[nums[i]] = 1;
for (int i = 0; i <= numsSize; i++)
{
if (hash[i] == 0)
return i;
}
return -1; //为了oj能过
序号283
题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
解析:一开始想到把 0 全部后移,这要移动很多次非 0 元素
- 重写数组
遍历数组,把非 0 元素从头重新写入,最后写 0 - 交换元素
遍历数组,把非 0 元素与最前面的 0 交换
如果 0 少,第一种更快;0 多,第二种更快
这里采用第二种
void moveZeroes(int* nums, int numsSize){
int pos = 0;
for (int i = 0; i < numsSize; i++)
{
if (nums[i] != 0)
{
int temp = nums[i];
nums[i] = nums[pos];
nums[pos] = temp;
pos++;
}
}
}
注:源自于题解
序号414
题目:给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。
解析:因为要求为 O ( n ) O(n) O(n),所以不能排序
- 遍历数组,更新前三大的数
初值设为最小,前三大的数不能重复
int thirdMax(int* nums, int numsSize){
long long ans[3];
for (int i = 0; i < 3; i++)
ans[i] = LLONG_MIN;
for (int i = 0; i < numsSize; i++)
{
if (nums[i] == ans[0] || nums[i] == ans[1] || nums[i] == ans[2])
continue;
if (nums[i] > ans[0])
{
ans[2] = ans[1];
ans[1] = ans[0];
ans[0] = nums[i];
}
else if (nums[i] > ans[1])
{
ans[2] = ans[1];
ans[1] = nums[i];
}
else if (nums[i] > ans[2])
ans[2] = nums[i];
}
if (ans[2] != LLONG_MIN)
return (int)ans[2];
else
return (int)ans[0];
}
序号448
题目:给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为
O
(
n
)
O(n)
O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
解析:先想到 hash,用不同数字记录出现否
- 用符号记录出现否
遍历数组,每个数对应的下标,下标的数取负。即用符号标记出现
用乘法标记为负
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize){
int* ans = (int*)malloc(sizeof(int) * numsSize);
int len = 0;
for (int i = 0; i < numsSize; i++)
{
int pos = abs(nums[i]) - 1; //因为从 1 开始,减 1 位
if (nums[pos] > 0)
nums[pos] *= -1;
}
for (int i = 0; i < numsSize; i++)
{
if (nums[i] > 0)
{
ans[len] = i + 1;
len++;
}
}
*returnSize = len;
return ans;
}
注:源自于题解
序号485
题目:给定一个二进制数组, 计算其中最大连续1的个数。
解析:
- 模拟连续
遇到 1,就累计。遇到 0,就重置
int findMaxConsecutiveOnes(int* nums, int numsSize){
int sum = 0;
int max = 0;
for (int i = 0; i < numsSize; i++)
{
if (nums[i] == 1)
sum += 1;
else
sum = 0;
if (sum > max)
max = sum;
}
return max;
}
注:如果数据 1 比较多,可以在等于 0 时再更新最大值,返回前更新一次
序号532
题目:给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对。这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k.
解析:
- 第一种用 hash
记录元素后,遍历数组找到符合条件的整数对 - 第二种用排序
排完序,k == 0,找重复的;否则,遍历找符合条件的
void DownAdjust(int* arr, int len, int pos)
{
int temp = arr[pos];
int child = pos * 2 + 1;
while (child < len)
{
if (child + 1 < len && arr[child] < arr[child + 1])
child++;
if (arr[child] > temp)
{
arr[pos] = arr[child];
pos = child;
}
else
break;
child = pos * 2 + 1;
}
arr[pos] = temp;
}
void HeapSort(int* arr, int len)
{
for (int i = len/2 - 1; i >= 0; i--)
DownAdjust(arr, len, i);
for (int i = len - 1; i > 0; i--)
{
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
DownAdjust(arr, i, 0);
}
}
int findPairs(int* nums, int numsSize, int k){
if (k < 0 || numsSize < 2)
return 0;
int cnt = 0;
HeapSort(nums, numsSize);
for (int i = 0; i < numsSize - 1; i++)
{
while (i < numsSize - 1 && nums[i] == nums[i+1])
i++;
if (k == 0)
{
if (i > 0 && nums[i] == nums[i-1])
++cnt;
}
else
{
for (int j = i + 1; j < numsSize; j++)
{
if (nums[j] - nums[i] == k)
{
cnt++;
break;
}
}
}
}
return cnt;
}