leetcode-1.两数之和
leetcode-1.两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
int *result = NULL;
for(int i = 0; i < numsSize - 1; i++){
for(int j = i + 1; j < numsSize; j++){
if(nums[i] + nums[j] == target){
result = (int*)malloc(sizeof(int)*2);
result[0] = i;
result[1] = j;
*returnSize = 2;
return result;
}
}
}
return result;
}
leetcode-4.寻找两个正序数组的中位数
leetcode-4.寻找两个正序数组的中位数(二分版)
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
if(nums1Size > nums2Size){
return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size); // 保证numsSize1 < nums2Size
}
int iMin = 0, iMax = nums1Size;
int i, j;
while(iMin <= iMax){
i = (iMin + iMax) / 2;
j = (nums1Size + nums2Size + 1) / 2 - i;
if(i != nums1Size && j != 0 && nums1[i] < nums2[j-1]){ // i 需要增大
iMin = i + 1;
}
else if(i != 0 && j != nums2Size && nums1[i-1] > nums2[j]){ // i 需要减小
iMax = i - 1;
}
else{
break;
}
}
int maxLeft = 0;
if(i == 0){
maxLeft = nums2[j-1];
}
else if(j == 0){
maxLeft = nums1[i-1];
}
else{
maxLeft = max(nums1[i-1], nums2[j-1]);
}
if((nums1Size + nums2Size) % 2 == 1){ // 奇数的话中位数在左半部分最右,不需要考虑右半部分
return maxLeft;
}
int minRight = 0;
if(i == nums1Size){
minRight = nums2[j];
}
else if(j == nums2Size){
minRight = nums1[i];
}
else{
minRight = min(nums1[i], nums2[j]);
}
return (maxLeft + minRight) / 2.0; //偶数结果为左边最大值与右边最小值的平均
}
时间复杂度:我们对较短的数组进行了二分查找,所以时间复杂度是O(log(min(m, n))
空间复杂度:只有一些固定的变量,和数组长度无关,所以空间复杂度是O(1)
leetcode-11.盛最多水的容器
leetcode-11.盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
#define min(a,b) (((a) < (b)) ? (a) : (b))
int maxArea(int* height, int heightSize){
int i = 0, j = heightSize - 1;
int mx = 0, high, width;
while(i < j){
high = min(height[i], height[j]);
width = j - i;
if(mx < high * width){
mx = high * width;
}
if(height[i] < height[j]){
i++;
}
else{
j--;
}
}
return mx;
}
时间复杂度:只需要双指针遍历一遍底边宽度,所以为O(n)
空间复杂度:变量i,j,mx等,使用常数额外空间,所以为O(1)
这题有个问题,如果存在相等的情况,如8,1,2,3,4,5,6,7,8,答案好像就不对了。
leetcode-15.三数之和(快速排序+双指针)
leetcode-15.三数之和(快速排序+双指针)
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
void quickSort(int* nums, int first, int end) // 快速排序
{
int temp, l, r;
if(first >= end)
return;
temp = nums[first];
l = first, r = end;
while(l < r){
while(l < r && temp <= nums[r])
r--;
if(l < r)
nums[l] = nums[r];
while(l < r && nums[l] <= temp)
l++;
if(l < r)
nums[r] = nums[l];
}
nums[l] = temp;
quickSort(nums, first, l - 1);
quickSort(nums, l + 1, end);
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
if(numsSize < 3)
return NULL;
quickSort(nums, 0, numsSize - 1);
int** f = (int**)malloc(sizeof(int*) * numsSize * numsSize);
*returnColumnSizes = (int**)malloc(sizeof(int*) * numsSize * numsSize);
*returnSize = 0;
for(int i = 0; i < numsSize - 2; i++){
if(nums[i] > 0)
break;
if(i != 0 && nums[i] == nums[i-1])
continue;
int left = i + 1, right = numsSize - 1;
while(left < right){
int sum = nums[i] + nums[right] + nums[left];
if(sum == 0){
f[*returnSize] = (int*)malloc(sizeof(int) * 3);
f[*returnSize][0] = nums[i];
f[*returnSize][1] = nums[right];
f[*returnSize][2] = nums[left];
(*returnColumnSizes)[*returnSize] = 3;
*returnSize = *returnSize + 1;
int a, b;
do{
a = left;
left = left + 1;
}while(a < right && nums[a] == nums[left]);
do{
b = right;
right = right - 1;
}while(b > left && nums[b] == nums[right]);
}
else if(sum < 0){
left++;
}
else{
right--;
}
}
}
return f;
}
时间复杂度:O(n2),其中 n 是数组 nums 的长度。
空间复杂度:O(logn)。我们忽略存储答案的空间,额外的排序的空间复杂度为 O(logn)。然而我们修改了输入的数组 nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了 nums 的副本并进行排序,空间复杂度为 O(n)。
leetcode-16.最接近的三数之和(快速排序+双指针)
leetcode-16.最接近的三数之和(快速排序+双指针)
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
void quickSort(int* nums, int first, int end) // 快速排序
{
if(first >= end)
return;
int temp = nums[first];
int l = first, r = end;
while(l < r){
while(l < r && temp <= nums[r])
r--;
if(l < r)
nums[l] = nums[r];
while(l < r && nums[l] <= temp)
l++;
if(l < r)
nums[r] = nums[l];
}
nums[l] = temp;
quickSort(nums, first, l - 1);
quickSort(nums, l + 1, end);
}
int threeSumClosest(int* nums, int numsSize, int target){
if(numsSize < 3)
return 0;
quickSort(nums, 0, numsSize - 1);
int mn = INT_MAX, ans;
for(int i = 0; i < numsSize - 2; i++){
int left = i + 1, right = numsSize - 1;
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
if(sum == target)
return sum;
if(mn > abs(sum - target)){
mn = abs(sum - target);
ans = sum;
}
if(sum > target){
int m;
do{
m = right;
right = right - 1;
}while(m > left && nums[m] == nums[right]);
}
else{
int n;
do{
n = left;
left = left + 1;
}while(n < right && nums[n] == nums[left]);
}
}
}
return ans;
}
时间复杂度:O(n2),其中 n 是数组 nums 的长度。
空间复杂度:O(logn)。排序需要使用 O(logn) 的空间。然而我们修改了输入的数组 nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了 nums 的副本并进行排序,此时空间复杂度为 O(n)。
leetcode-18.四数之和(快速排序+双指针)
leetcode-18.四数之和(快速排序+双指针)
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
void quickSort(int* nums, int start, int end)
{
if(start >= end)
return;
int temp = nums[start];
int l = start, r = end;
while(l < r){
while(l < r && temp <= nums[r])
r--;
if(l < r)
nums[l] = nums[r];
while(l < r && nums[l] <= temp)
l++;
if(l < r)
nums[r] = nums[l];
}
nums[l] = temp;
quickSort(nums, start, l - 1);
quickSort(nums, l + 1, end);
}
int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes){
quickSort(nums, 0, numsSize - 1);
int** f = (int**)malloc(sizeof(int*) * 1001);
*returnColumnSizes = (int*)malloc(sizeof(int) * 1001);
*returnSize = 0;
if(numsSize < 4 || nums == NULL)
return NULL;
for(int i = 0; i < numsSize - 3; i++){
if(i > 0 && nums[i] == nums[i-1])
continue;
if((long)nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target)
break;
if((long)nums[i] + nums[numsSize-3] + nums[numsSize-2] + nums[numsSize-1] < target)
continue;
for(int j = i + 1; j < numsSize - 2; j++){
if(j > i + 1 && nums[j] == nums[j-1])
continue;
if((long)nums[i] + nums[j] + nums[j+1] + nums[j+2] > target)
break;
if((long)nums[i] + nums[j] + nums[numsSize-2] + nums[numsSize-1] < target)
continue;
int left = j + 1, right = numsSize - 1;
while(left < right){
long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
if(sum == target){
f[*returnSize] = (int*)malloc(sizeof(int) * 4);
f[*returnSize][0] = nums[i];
f[*returnSize][1] = nums[j];
f[*returnSize][2] = nums[left];
f[*returnSize][3] = nums[right];
(*returnColumnSizes)[*returnSize] = 4;
*returnSize = *returnSize + 1;
int a, b;
do{
a = left;
left = left + 1;
}while(a < right && nums[a] == nums[left]);
do{
b = right;
right = right - 1;
}while(b > left && nums[b] == nums[right]);
}
else if(sum > target){
right = right - 1;
}
else{
left = left + 1;
}
}
}
}
return f;
}
leetcode-26.删除有序数组中的重复项
leetcode-26.删除有序数组中的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
将最终结果插入 nums 的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
int removeDuplicates(int* nums, int numsSize){
int pre = nums[0], l = 1;
for(int i = 1; i < numsSize; i++){
if(nums[i] != pre){
nums[l] = nums[i];
pre = nums[l];
l = l + 1;
}
}
return l;
}
时间复杂度:O(n),其中 n 是数组的长度。
空间复杂度:O(1)。只需要使用常数的额外空间。
leetcode-27.移除元素
leetcode-27.移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
int removeElement(int* nums, int numsSize, int val){
int left = 0, right = numsSize;
while(left < right){
if(nums[left] == val){
nums[left] = nums[right-1];
right--;
}
else{
left++;
}
}
return left;
}
时间复杂度:O(n),其中 n 为序列的长度。我们只需要遍历该序列至多一次。
空间复杂度:O(1)。我们只需要常数的空间保存若干变量。
leetcode-33.搜索旋转排序数组(二分查找)
leetcode-33.搜索旋转排序数组(二分查找)
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
int search(int* nums, int numsSize, int target){
int i = 0, j = numsSize - 1;
while(i <= j){
int mid = (i + j) / 2;
if(nums[mid] == target){
return mid;
}
if(nums[mid] < nums[j]){ // 当(mid,j]区间有序时
if(nums[mid] < target && target <= nums[j]){
i = mid + 1;
}
else{
j = mid - 1;
}
}
else{ // 当[i,mid)区间有序时
if(nums[i] <= target && target <nums[mid]){
j = mid - 1;
}
else{
i = mid + 1;
}
}
}
return -1;
}
时间复杂度:O(logn),其中 n为 nums 数组的大小。整个算法时间复杂度即为二分查找的时间复杂度 O(logn)。
空间复杂度:O(1) 。我们只需要常数级别的空间存放变量。
leetcode-34.在排序数组中查找元素的第一个和最后一个位置(二分查找)
leetcode-34.在排序数组中查找元素的第一个和最后一个位置(二分查找)
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
int binarySearch(int* nums, int numsSize, int target, bool lower)
{
int left = 0, right = numsSize - 1, ans = numsSize;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] > target || (lower && nums[mid] >= target)){
right = mid - 1;
ans = mid;
}
else{
left = mid + 1;
}
}
return ans;
}
int* searchRange(int* nums, int numsSize, int target, int* returnSize){
int lindex = binarySearch(nums, numsSize, target, true);
int rindex = binarySearch(nums, numsSize, target, false) - 1;
int* ret = malloc(sizeof(int) * 2);
*returnSize = 2;
if(lindex <= rindex && rindex < numsSize && nums[lindex] == target && nums[rindex] == target){
ret[0] = lindex;
ret[1] = rindex;
}
else{
ret[0] = -1;
ret[1] = -1;
}
return ret;
}
时间复杂度:O(logn),其中 n 为数组的长度。二分查找的时间复杂度为 O(logn),一共会执行两次,因此总时间复杂度为 O(logn)。
空间复杂度:O(1) 。只需要常数空间存放若干变量。
leetcode-35.搜索插入位置(二分查找)
leetcode-35.搜索插入位置(二分查找)
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
int searchInsert(int* nums, int numsSize, int target){
int i = 0, j = numsSize - 1;
while(i <= j){
int mid = (i + j) / 2;
if(nums[mid] < target){
i = mid + 1;
}
else{
j = mid - 1;
}
}
return i;
}
时间复杂度:O(logn),其中 n 为数组的长度。二分查找所需的时间复杂度为 O(logn)。
空间复杂度:O(1)。我们只需要常数空间存放若干变量。
leetcode-41.缺失的第一个正数
leetcode-41.缺失的第一个正数
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
方法一:哈希表
int firstMissingPositive(int* nums, int numsSize){
for(int i = 0; i < numsSize; i++){
if(nums[i] <= 0){
nums[i] = numsSize + 1;
}
}
for(int i = 0; i < numsSize; i++){
int num = abs(nums[i]);
if(num <= numsSize){
nums[num - 1] = -abs(nums[num - 1]);
}
}
for(int i = 0; i < numsSize; i++){
if(nums[i] > 0){
return i + 1;
}
}
return numsSize + 1;
}
时间复杂度:O(N),其中 N 是数组的长度。
空间复杂度:O(1)。
方法二:置换
int firstMissingPositive(int* nums, int numsSize){
for(int i = 0; i < numsSize; i++){
while(nums[i] > 0 && nums[i] <= numsSize && nums[nums[i] - 1] != nums[i] ){
int t = nums[nums[i] - 1];
nums[nums[i] - 1] = nums[i];
nums[i] = t;
}
}
for(int i = 0; i < numsSize; i++){
if(nums[i] != i + 1){
return i + 1;
}
}
return numsSize + 1;
}
时间复杂度:O(N),其中 N 是数组的长度。
空间复杂度:O(1)。
leetcode-42.接雨水
leetcode-42.接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
#define max(a,b) (a > b) ? a : b
int trap(int* height, int heightSize){
int ans = 0, left = 0, right = heightSize - 1;
int maxLeft = height[left], maxRight = height[right];
while(left < right){
maxLeft = max(maxLeft, height[left]);
maxRight = max(maxRight, height[right]);
if(maxLeft < maxRight){
ans += maxLeft - height[left];
left++;
}
else{
ans += maxRight - height[right];
right--;
}
}
return ans;
}
时间复杂度:O(n),其中 n 是数组height 的长度。两个指针的移动总次数不超过 n。
空间复杂度:O(1)。只需要使用常数的额外空间。
leetcode-45.跳跃游戏 II
leetcode-45.跳跃游戏 II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
int jump(int* nums, int numsSize){
int end = 0; // 边界
int maxPosition = 0; // 所能跳到的最大位置
int step = 0; // 记录跳跃数
for(int i = 0; i < numsSize - 1; i++){
maxPosition = maxPosition > nums[i] + i ? maxPosition : nums[i] + i; // 找能跳到的最远的
if(i == end){ // 遇到边界,就更新边界,并且步数加一
end = maxPosition;
step++;
}
}
return step;
}
时间复杂度:O(n),其中 n 是数组长度。
空间复杂度:O(1)。
leetcode-46.全排列
leetcode-46.全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
int vis[1024] = {0};
void dfs(int* nums, int numsSize, int n, int arr[], int** res, int* ind, int** returnColumnSizes)
{
if(n == numsSize){
res[*ind] = (int*)malloc(sizeof(int) * (numsSize + 5));
for(int i = 0; i < numsSize; i++){
res[*ind][i] = arr[i];
}
(*returnColumnSizes)[*ind] = numsSize;
(*ind)++;
return ;
}
for(int i = 0; i < numsSize; i++){
if(vis[i] == 1) continue;
vis[i] = 1;
arr[n] = nums[i];
dfs(nums, numsSize, n + 1, arr, res, ind, returnColumnSizes);
vis[i] = 0;
}
}
int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
*returnSize = 0;
if(numsSize == 0)
return 0;
int cnt = 1, k = 1;
while(k <= numsSize){
cnt = cnt * k;
k++;
}
int** res = (int**)malloc(sizeof(int**) * cnt);
*returnColumnSizes = (int*)malloc(sizeof(int) * cnt);
int arr[numsSize + 5], ind = 0;
dfs(nums, numsSize, 0, arr, res, &ind, returnColumnSizes);
*returnSize = ind;
return res;
}
时间复杂度:O(n×n!),其中 n 为序列的长度。而对于dfs 调用的每个叶结点(共 n! 个),我们需要将当前答案使用 O(n) 的时间复制到答案数组中,相乘得时间复杂度为 O(n×n!)。因此时间复杂度为 O(n×n!)。
空间复杂度:O(n),其中 n 为序列的长度。除答案数组以外,递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,这里可知递归调用深度为 O(n)。
leetcode-47.全排列 II
leetcode-47.全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
int vis[1024] = {0};
void quickSort(int* nums, int start, int end)
{
if(start >= end)
return ;
int temp = nums[start];
int l = start, r = end;
while(l < r){
while(l < r && temp <= nums[r])
r--;
if(l < r)
nums[l] = nums[r];
while(l < r && nums[l] <= temp)
l++;
if(l < r)
nums[r] = nums[l];
}
nums[l] = temp;
quickSort(nums, start, l - 1);
quickSort(nums, l + 1, end);
}
void dfs(int* nums, int numsSize, int n, int** res, int arr[], int* ind, int** returnColumnSizes)
{
if(n == numsSize){
res[*ind] = (int*)malloc(sizeof(int) * (numsSize + 5));
for(int i = 0; i < numsSize; i++){
res[*ind][i] = arr[i];
}
(*returnColumnSizes)[*ind] = numsSize;
(*ind)++;
}
for(int i = 0; i < numsSize; i++){
if(vis[i] == 1) continue;
vis[i] = 1;
arr[n] = nums[i];
dfs(nums, numsSize, n + 1, res, arr, ind, returnColumnSizes);
vis[i] = 0;
while(i < numsSize - 1 && nums[i] == nums[i+1]) i++;
}
}
int** permuteUnique(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
*returnSize = 0;
if(numsSize == 0)
return 0;
int cnt = 1, k = 1;
while(k <= numsSize){
cnt = cnt * k;
k++;
}
int** res = (int**)malloc(sizeof(int*) * cnt);
*returnColumnSizes = (int*)malloc(sizeof(int) * cnt);
int arr[numsSize + 5], ind = 0;
quickSort(nums, 0, numsSize - 1);
dfs(nums, numsSize, 0, res, arr, &ind, returnColumnSizes);
*returnSize = ind;
return res;
}
时间复杂度:O(n×n!),其中 n 为序列的长度。
空间复杂度:O(n)。我们需要 O(n) 的标记数组,同时在递归的时候栈深度会达到 O(n),因此总空间复杂度为 O(n + n) = O(2n) = O(n)。
leetcode-53.最大子数组和
leetcode-53.最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
int maxSubArray(int* nums, int numsSize)
{
int pre = 0, maxAns = nums[0];
for(int i = 0; i < numsSize; i++){
pre = fmax(pre + nums[i], nums[i]);
maxAns = fmax(maxAns, pre);
}
return maxAns;
}
时间复杂度:O(n),其中 n 为 nums 数组的长度。我们只需要遍历一遍数组即可求得答案。
空间复杂度:O(1)。我们只需要常数空间存放若干变量。
leetcode-55.跳跃游戏
leetcode-55.跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
bool canJump(int* nums, int numsSize){
int end = 0;
int maxPosition = 0;
for(int i = 0; i < numsSize; i++){
maxPosition = maxPosition > nums[i] + i ? maxPosition : nums[i] + i;
if(i == end){
end = maxPosition;
}
}
if(end >= numsSize - 1)
return true;
else
return false;
}
时间复杂度:O(n),其中 n 为数组的大小。只需要访问 nums 数组一遍,共 n 个位置。
空间复杂度:O(1),不需要额外的空间开销。
leetcode-66.加一
leetcode-66.加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
int* plusOne(int* digits, int digitsSize, int* returnSize){
int* f = (int*)malloc(sizeof(int) * 150);
for(int i = digitsSize - 1; i >= 0; i--){
if(digits[i] != 9){
digits[i]++;
*returnSize = digitsSize;
return digits;
}
else{
digits[i] = 0;
}
}
f[0] = 1;
for(int i = 1; i <= digitsSize; i++){
f[i] = digits[i - 1];
}
*returnSize = digitsSize + 1;
return f;
}
时间复杂度:O(n),其中 n 是数组 digits 的长度。
空间复杂度:O(n)。
leetcode-75.颜色分类
leetcode-75.颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
void sortColors(int* nums, int numsSize)
{
int l = 0, r = numsSize - 1;
for(int i = 0; i < numsSize; i++){
while(i <= r && nums[i] == 2){
swap(&nums[i], &nums[r]);
r--;
}
if(nums[i] == 0){
swap(&nums[l], &nums[i]);
l++;
}
}
}
时间复杂度:O(n),其中 n 是数组nums 的长度。
空间复杂度:O(1)。
leetcode-77.组合
leetcode-77.组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
int vis[100] = {0};
void dfs(int n, int k, int* ind, int** returnColumnSizes, int** ans, int arr[], int t, int s)
{
if(k == t){
ans[*ind] = (int*)malloc(sizeof(int) * (k + 5));
for(int i = 0; i < k; i++){
ans[*ind][i] = arr[i];
}
(*returnColumnSizes)[*ind] = k;
(*ind)++;
}
for(int i = s; i <= n; i++){
vis[i] = 1;
arr[t] = i;
dfs(n, k, ind, returnColumnSizes, ans, arr, t + 1, i + 1);
vis[i] = 0;
}
}
int** combine(int n, int k, int* returnSize, int** returnColumnSizes){
if(k == 0)
return 0;
int ind = 0, arr[5000];
int** ans = (int**)malloc(sizeof(int*) * 5000);
*returnColumnSizes = (int*)malloc(sizeof(int) * 5000);
dfs(n, k, &ind, returnColumnSizes, ans, arr, 0, 1);
*returnSize = ind;
return ans;
}
leetcode-78.子集
leetcode-78.子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
int** ans;
int* ansColSize;
int ansSize;
int* t;
int tSize;
void dfs(int cur, int* nums, int numsSize)
{
if(cur == numsSize){
int* tmp = malloc(sizeof(int) * tSize);
memcpy(tmp, t, sizeof(int) * tSize);
ansColSize[ansSize] = tSize;
ans[ansSize++] = tmp;
return ;
}
t[tSize++] = nums[cur];
dfs(cur + 1, nums, numsSize);
tSize--;
dfs(cur + 1, nums, numsSize);
}
int** subsets(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
ans = malloc(sizeof(int*) * (1 << numsSize));
ansColSize = malloc(sizeof(int) * (1 << numsSize));
t = malloc(sizeof(int) * numsSize);
*returnSize = 1 << numsSize;
ansSize = tSize = 0;
dfs(0, nums, numsSize);
*returnColumnSizes = ansColSize;
return ans;
}
时间复杂度:O(n × 2n)。一共2n个状态,每种状态需要 O(n) 的时间来构造子集。
空间复杂度:O(n)。临时数组 t 的空间代价是 O(n),递归时栈空间的代价为 O(n)。
leetcode-80.删除有序数组中的重复项 II
leetcode-80.删除有序数组中的重复项 II
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
int removeDuplicates(int* nums, int numsSize){
int t = 1, pre = nums[0], idx = 0;
for(int i = 1; i < numsSize; i++){
if(nums[i] == pre){
t++;
if(t <= 2){
idx = idx + 1;
nums[idx] = nums[i];
}
}
else{
pre = nums[i];
t = 1;
idx = idx + 1;
nums[idx] = nums[i];
}
}
return idx + 1;
}
时间复杂度:O(n)。
空间复杂度:O(1)。
leetcode-81.搜索旋转排序数组 II
leetcode-81.搜索旋转排序数组 II
已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。
给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。
你必须尽可能减少整个操作步骤。
bool search(int* nums, int numsSize, int target){
int left = 0, right = numsSize - 1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == target){
return true;
}
if(nums[left] == nums[mid] && nums[mid] == nums[right]){
left++;
right--;
}
else if(nums[mid] <= nums[right]){
if(nums[mid] < target && target <= nums[right]){
left = mid + 1;
}
else{
right = mid - 1;
}
}
else{
if(nums[left] <= target && target < nums[mid]){
right = mid - 1;
}
else{
left = mid + 1;
}
}
}
return false;
}
时间复杂度:O(n),其中 n 是数组 nums 的长度。最坏情况下数组元素均相等且不为 target,我们需要访问所有位置才能得出结果。
空间复杂度:O(1)。
leetcode-88.合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int t1 = m - 1, t2 = n - 1;
int tail = m + n - 1;
while(t1 >= 0 || t2 >= 0){
if(t1 == -1){
nums1[tail--] = nums2[t2--];
}
else if(t2 == -1){
nums1[tail--] = nums1[t1--];
}
else if(nums1[t1] < nums2[t2]){
nums1[tail--] = nums2[t2--];
}
else{
nums1[tail--] = nums1[t1--];
}
}
}
时间复杂度:O(m + n)。指针移动单调递减,最多移动 m+n 次,因此时间复杂度为 O(m+n)。
空间复杂度:O(1)。直接对数组 nums1 原地修改,不需要额外空间。
leetcode-152.乘积最大子数组
leetcode-152.乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
int maxProduct(int* nums, int numsSize){
int minF = nums[0], maxF = nums[0], ans = nums[0];
for(int i = 1; i < numsSize; i++){
int mn = minF, mx = maxF;
maxF = fmax(nums[i] * mx, fmax(nums[i], nums[i] * mn));
minF = fmin(nums[i] * mn, fmin(nums[i], nums[i] * mx));
ans = fmax(maxF, ans);
}
return ans;
}
时间复杂度:程序一次循环遍历了 nums,故渐进时间复杂度为 O(n)。
空间复杂度:优化后只使用常数个临时变量作为辅助空间,与 n 无关,故渐进空间复杂度为 O(1)。
leetcode-153.寻找旋转排序数组中的最小值
leetcode-153.寻找旋转排序数组中的最小值
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
- 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
- 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
int findMin(int* nums, int numsSize){
int left = 0, right = numsSize - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] < nums[right]){
right = mid;
}
else{
left = mid + 1;
}
}
return nums[left];
}
时间复杂度:时间复杂度为 O(logn),其中 n 是数组 nums 的长度。在二分查找的过程中,每一步会忽略一半的区间,因此时间复杂度为 O(logn)。
空间复杂度:O(1)。
leetcode-154. 寻找旋转排序数组中的最小值 II
leetcode-154. 寻找旋转排序数组中的最小值 II
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
- 若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
- 若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须尽可能减少整个过程的操作步骤。
int findMin(int* nums, int numsSize){
int left = 0, right = numsSize - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] < nums[right]){
right = mid;
}
else if(nums[mid] > nums[right]){
left = mid + 1;
}
else{
right--;
}
}
return nums[left];
}
时间复杂度:平均时间复杂度为 O(logn),其中 n 是数组 nums 的长度。如果数组是随机生成的,那么数组中包含相同元素的概率很低,在二分查找的过程中,大部分情况都会忽略一半的区间。而在最坏情况下,如果数组中的元素完全相同,那么 while 循环就需要执行 n 次,每次忽略区间的右端点,时间复杂度为 O(n)。
空间复杂度:O(1)。
leetcode-162.寻找峰值
leetcode-162.寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = − ∞ -\infty −∞。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
int findPeakElement(int* nums, int numsSize){
int left = 0, right = numsSize - 1;
while(left < right){
int mid = left + ((right - left) >> 1);
if(nums[mid] < nums[mid + 1]){
left = mid + 1;
}
else{
right = mid;
}
}
return left;
}
时间复杂度:O(logn),其中 n 是数组 nums 的长度。
空间复杂度:O(1)。
leetcode-169.多数元素
leetcode-169.多数元素
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
int majorityElement(int* nums, int numsSize){
int num = nums[0], cnt = 1;
for(int i = 1; i < numsSize; i++){
if(num == nums[i]){
cnt++;
}
else if(cnt == 1){
num = nums[i];
}
else{
cnt--;
}
}
cnt = 0;
for(int i = 0; i < numsSize; i++){
if(num == nums[i]){
cnt++;
}
}
return cnt > numsSize / 2 ? num : -1;
}
时间复杂度:O(n)。
空间复杂度:O(1)。
leetcode-189.轮转数组
leetcode-189.轮转数组
给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
void reverse(int* nums, int start, int end)
{
while(start < end){
swap(&nums[start], &nums[end]);
start++;
end--;
}
}
void rotate(int* nums, int numsSize, int k){
k = k % numsSize;
reverse(nums, 0, numsSize - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, numsSize - 1);
}
时间复杂度:O(n),其中 n 为数组的长度。每个元素被翻转两次,一共 n 个元素,因此总时间复杂度为 O(2n)=O(n)。
空间复杂度:O(1)。
leetcode-209.长度最小的子数组
leetcode-209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
int minSubArrayLen(int target, int* nums, int numsSize){
if(numsSize == 0){
return 0;
}
int ans = INT_MAX;
int start = 0, end = 0;
int sum = 0;
while(end < numsSize){
sum += nums[end];
while(sum >= target){
ans = fmin(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == INT_MAX ? 0 : ans;
}
时间复杂度:O(n),其中 n 是数组的长度。指针 start 和 end 最多各移动 n 次。
空间复杂度:O(1)。
leetcode-217.存在重复元素
leetcode-217.存在重复元素
给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。
int cmp(const void* _a, const void* _b) {
int a = *(int*)_a, b = *(int*)_b;
return a - b;
}
bool containsDuplicate(int* nums, int numsSize){
qsort(nums, numsSize, sizeof(int), cmp);
// quicksort(nums, 0, numsSize - 1); // 不知道为什么自己写的快排会超时
for(int i = 0; i < numsSize - 1; i++){
if(nums[i] == nums[i+1]){
return true;
}
}
return false;
}
时间复杂度:O(NlogN),其中 N 为数组的长度。需要对数组进行排序。
空间复杂度:O(logN),其中 N 为数组的长度。注意我们在这里应当考虑递归调用栈的深度。
leetcode-238.除自身以外数组的乘积
leetcode-238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
int* productExceptSelf(int* nums, int numsSize, int* returnSize){
int* answer = (int*)malloc(sizeof(int) * numsSize);
answer[0] = 1;
for(int i = 1; i < numsSize; i++){
answer[i] = answer[i - 1] * nums[i - 1];
}
int r = 1;
for(int i = numsSize - 1; i >= 0; i--){
answer[i] = answer[i] * r;
r = r * nums[i];
}
*returnSize = numsSize;
return answer;
}
时间复杂度:O(N),其中 N 指的是数组 nums 的大小。分析与方法一相同。
空间复杂度:O(1),输出数组不算进空间复杂度中,因此我们只需要常数的空间存放变量。
leetcode-274.H 指数
leetcode-274.H 指数
给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。
根据维基百科上 h 指数的定义:h 代表“高引用次数”,一名科研人员的 h 指数 是指他(她)的 (n 篇论文中)总共 有 h 篇论文分别被引用了 至少 h 次。且其余的 n - h 篇论文每篇被引用次数 不超过 h 次。
如果 h 有多种可能的值,h 指数 是其中最大的那个。
int hIndex(int* citations, int citationsSize){
int n = citationsSize, tot = 0;
int cnt[n + 1];
memset(cnt, 0, sizeof(cnt));
for(int i = 0; i < n; i++){
if(citations[i] > n){
cnt[n]++;
}
else{
cnt[citations[i]]++;
}
}
for(int i = n; i >= 0; i--){
tot += cnt[i];
if(tot >= i){
return i;
}
}
return 0;
}
时间复杂度:O(n),其中 n 为数组 citations 的长度。需要遍历数组 citations 一次,以及遍历长度为 n+1 的数组 cnt 一次。
空间复杂度:O(n),其中 n 为数组 citations 的长度。需要创建长度为 n+1 的数组 cnt。
leetcode-275.H 指数 II(二分查找)
leetcode-275.H 指数 II(二分查找)
给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数,citations 已经按照 升序排列 。计算并返回该研究者的 h 指数。
h 指数的定义:h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (n 篇论文中)总共有 h 篇论文分别被引用了至少 h 次。且其余的 n - h 篇论文每篇被引用次数 不超过 h 次。
提示:如果 h 有多种可能的值,h 指数 是其中最大的那个。
请你设计并实现对数时间复杂度的算法解决此问题。
int hIndex(int* citations, int citationsSize){
int left = 0, right = citationsSize - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(citations[mid] >= citationsSize - mid){
right = mid - 1;
}
else{
left = mid + 1;
}
}
return citationsSize - left;
}
时间复杂度:O(logn),其中 n 为数组 citations 的长度。二分查找的时间复杂度为 O(logn)。
空间复杂度:O(1)。
leetcode-278.第一个错误的版本
leetcode-278.第一个错误版本
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
int firstBadVersion(int n) {
int left = 1, right = n;
while(left < right){
int mid = left + (right - left) / 2;
if(isBadVersion(mid)){
right = mid;
}
else{
left = mid + 1;
}
}
return left;
}
时间复杂度:O(logn),其中 n 是给定版本的数量。
空间复杂度:O(1)。我们只需要常数的空间保存若干变量。
leetcode-283.移动零
leetcode-283.移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
void moveZeroes(int* nums, int numsSize){
int left = 0, right = 0;
while(right < numsSize){
if(nums[right]){
swap(nums + left, nums + right);
left++;
}
right++;
}
}
时间复杂度:O(n),其中 n 为序列长度。每个位置至多被遍历两次。
空间复杂度:O(1)。只需要常数的空间存放若干变量。
leetcode-334.递增子序列
leetcode-334.递增子序列
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
bool increasingTriplet(int* nums, int numsSize){
if(numsSize < 3){
return false;
}
int first = nums[0], second = INT_MAX;
for(int i = 1; i < numsSize; i++){
if(nums[i] > second){
return true;
}
else if(nums[i] > first){
second = nums[i];
}
else{
first = nums[i];
}
}
return false;
}
时间复杂度:O(n),其中 n 是 nums 数组的长度。需要遍历数组一次。
空间复杂度:O(1)。