数据结构与算法1(LeetCode)
Posted 康x呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法1(LeetCode)相关的知识,希望对你有一定的参考价值。
数据结构与算法1 LeetCode
- 剑指 Offer22. 链表中倒数第k个节点
- 165.比较版本号
- 面试题 17.14. 最小K个数
- 704. 二分查找
- 278. 第一个错误的版本
- 35. 搜索插入位置
- 剑指 Offer 10- I. 斐波那契数列
- 977. 有序数组的平方
- 189. 旋转数组
- 283. 移动零
- 1221. 分割平衡字符串
- 1167. 两数之和 II - 输入有序数组
剑指 Offer22. 链表中倒数第k个节点
OJ链接:https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
给定一个链表:
1->2->3->4->5, 和 k = 2.
返回链表 4->5.
代码:
思路:
先让p指针走k步;
然后让p指针和head指针一起前进
当p走到尽头时,返回head
// An highlighted block
struct ListNode* getKthFromEnd(struct ListNode* head, int k)
struct ListNode* p = head;
while(k--)
p = p->next;
while(p)
p = p->next;
head = head->next;
return head;
快慢指针法:
构造快慢指针,这两个指针之间相距K个距离,然后快指针走到尾了,慢指针正好在倒数K的位置
// An highlighted block
struct ListNode* getKthFromEnd(struct ListNode* head, int k)
if(head==NULL)return head;
struct ListNode* p=head;
struct ListNode* q=head;
for(int i=0;i<k;i++)
p=p->next;//构造两个指针之间相距为K
while(p!=NULL)
p=p->next;//快指针走到尾了
q=q->next; // 慢指针正好在倒数K的位置
return q;
165.比较版本号
OJ链接:https://leetcode-cn.com/problems/compare-version-numbers/
题目:给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 ‘.’ 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。
输入:version1 = “1.01”, version2 = “1.001”
输出:0
解释:忽略前导零,“01” 和 “001” 都表示相同的整数 “1”
示例 2:
输入:version1 = “1.0”, version2 = “1.0.0”
输出:0
解释:version1 没有指定下标为 2 的修订号,即视为 “0”
示例 3:
输入:version1 = “0.1”, version2 = “1.1”
输出:-1
解释:version1 中下标为 0 的修订号是 “0”,version2 中下标为 0 的修订号是 “1” 。0 < 1,所以 version1 < version2
示例 4:
输入:version1 = “1.0.1”, version2 = “1”
输出:1
示例 5:
输入:version1 = “7.5.2.4”, version2 = “7.5.3”
输出:-1
代码:
思路:遇到'.'和字符串结束时,比较下版本大小;
如果程序没有返回,就会返回0;
// An highlighted block
int compareVersion(char * version1, char * version2)
int len1 = strlen(version1);
int len2 = strlen(version2);
int i = 0;
int j = 0;
while (i < len1 || j < len2)
int num1 = 0;
int num2 = 0;
while (i < len1 && version1[i] != '.')
num1 = num1 * 10 + (version1[i++] - '0');
while (j < len2 && version2[j] != '.')
num2 = num2 * 10 + (version2[j++] - '0');
if (num1 < num2)
return -1;
else if (num1 > num2)
return 1;
i++;
j++;
return 0;
面试题 17.14. 最小K个数
OJ链接:https://leetcode-cn.com/problems/smallest-k-lcci/
题目:设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]
代码:
基本思路:采用最大堆维护arr数组前k个元素对剩余每一个元素,如果比堆中最大的元素还大,则用此元素
替换最大元素这样堆中就一直保持最小的k个元素
// An highlighted block
void SiftDown(int a[], int i, int n)
//对data[0,n)所形成的最大堆中,索引为 i 的元素,执行下沉操作,即维持最大堆
while( (2*i+1) < n )
//结点i不是叶子结点 ,即最少有左孩子
//找出i左右孩子的最大值
int j = 2*i+1;
if((2*i+2) < n && a[2*i+1] < a[2*i+2]) //右孩子存在
j = 2*i + 2;
if(a[i] >= a[j]) //孩子结点的最大值大于该结点,交换
break;
int t = a[i];
a[i] = a[j];
a[j] = t;
i = j;
int* smallestK(int* arr, int arrSize, int k, int* returnSize)
if(k<=0)
*returnSize = 0;
return NULL;
//将前 k个元素拷贝到 st数组
int *sk = (int*)malloc(sizeof(int)*k);
for(int i=0; i<k; i++)
sk[i] = arr[i];
//将sk数组元素按照最大堆排列,heapify
for(int i=k/2-1; i>=0; i--)
SiftDown(sk,i,k);
//用堆中最大元素与剩余元素一一比较
for(int i=k; i<arrSize; i++)
if(sk[0] > arr[i])
sk[0] = arr[i]; //若大于,则替换最大堆,则sk中保存的是前i个元素中最小的k个元素
SiftDown(sk,0,k);
//返回sk数组
*returnSize = k;
return sk;
704. 二分查找
OJ链接:https://leetcode-cn.com/problems/binary-search/
题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
代码:
二分查找是一种基于比较目标值和数组中间元素的教科书式算法。
如果目标值等于中间元素,则找到目标值。
如果目标值较小,继续在左侧搜索。
如果目标值较大,则继续在右侧搜索。
// An highlighted block
int search(int* nums, int numsSize, int target)
int left = 0;
int right = numsSize;
while (left < right)
int index = left + (right - left) / 2;
if (nums[index] == target)
return index;
else if (nums[index] > target )
right = index;
else
left = index + 1;
return -1;
278. 第一个错误的版本
OJ链接:https://leetcode-cn.com/problems/first-bad-version/
题目:你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例 1:
输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
示例 2:
输入:n = 1, bad = 1
输出:1
代码:
与上题二分法思想类似
// An highlighted block
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
int firstBadVersion(int n)
int left = 1, right = n;
while (left < right) // 循环直至区间左右端点相同
int mid = left + (right - left) / 2; // 防止计算时溢出
if (isBadVersion(mid))
right = mid; // 答案在区间 [left, mid] 中
else
left = mid + 1; // 答案在区间 [mid+1, right] 中
// 此时有 left == right,区间缩为一个点,即为答案
return left;
35. 搜索插入位置
OJ链接:https://leetcode-cn.com/problems/search-insert-position/
题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
示例 4:
输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:
输入: nums = [1], target = 0
输出: 0
代码:
先设定左侧下标left和右侧下标right,再计算中间下标mid
-每次根据nums[mid]和 target 之间的大小进行判断,相等则直接返回下标,nums[mid]< target则
left右移,nums[mid] > target 则right左移
-查找结束如果没有相等值则返回left,该值为插入位置
// An highlighted block
int searchInsert(int* nums, int numsSize, int target)
int left = 0, right = numsSize - 1, ans = numsSize;
while (left <= right)
int mid = left + (right - left) / 2;
if (target <= nums[mid])
ans = mid;
right = mid - 1;
else
left = mid + 1;
return ans;
剑指 Offer 10- I. 斐波那契数列
OJ链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/
题目:写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:1
示例 2:
输入:n = 5
输出:5
代码:
// A code block
var foo = 'bar';
// An highlighted block
int fib(int n)
long *dp = (long *)malloc(101*sizeof(long));
dp[0] = 0;
dp[1] = 1;
for (int i=2;i<=n;i++)
dp[i] = (dp[i-1] + dp[i-2]) % 1000000007;
return dp[n];
977. 有序数组的平方
OJ链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array/
题目:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
代码:
双指针法
我们可以使用两个指针分别指向位置 00 和 n-1n−1,每次比较两个指针对应的数,选择较大的那个逆序
放入答案并移动指针。这种方法无需处理某一指针移动至边界的情况。
// An highlighted block
int* sortedSquares(int* nums, int numsSize, int* returnSize)
int* ans = malloc(sizeof(int) * numsSize);
*returnSize = numsSize;
for (int i = 0, j = numsSize - 1, pos = numsSize - 1; i <= j;)
if (nums[i] * nums[i] > nums[j] * nums[j])
ans[pos] = nums[i] * nums[i];
++i;
else
ans[pos] = nums[j] * nums[j];
--j;
--pos;
return ans;
189. 旋转数组
OJ链接:https://leetcode-cn.com/problems/rotate-array/
题目:给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
代码:
我们可以使用额外的数组来将每个元素放至正确的位置。用 nn 表示数组的长度,我们遍历原数组,将原数组
下标为 ii 的元素放至新数组下标为 (i+k)\\bmod n(i+k)modn 的位置,最后将新数组拷贝至原数组即可。
// An highlighted block
void rotate(int* nums, int numsSize, int k)
int newArr[numsSize];
for (int i = 0; i < numsSize; ++i)
newArr[(i + k) % numsSize] = nums[i];
for (int i = 0; i < numsSize; ++i)
nums[i] = newArr[i];
283. 移动零
OJ链接:https://leetcode-cn.com/problems/move-zeroes/
题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
代码:
双指针法:
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。右指针不断向右移动,
每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
**左指针左边均为非零数**
**右指针左边直到左指针处均为零**
因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。
// An highlighted block
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++;
下面为普通的方法较易理解
// An highlighted block
void moveZeroes(int *nums, int numsSize)
int i=0,place=0;
while(i<numsSize)
if(nums[i]!=0)
nums[place++]=nums[i];
i++;
for(int i=place;i<numsSize;i++)
nums[i]=0;
1221. 分割平衡字符串
OJ链接:https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/
题目:在一个 平衡字符串 中,‘L’ 和 ‘R’ 字符的数量是相同的。给你一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。
注意:分割得到的每个字符串都必须是平衡字符串。
返回可以通过分割得到的平衡字符串的 最大数量 。
示例 1:
输入:s = “RLRRLLRLRL”
输出:4
解释:s 可以分割为 “RL”、“RRLL”、“RL”、“RL” ,每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’ 。
示例 2:
输入:s = “RLLLLRRRLR”
输出:3
解释:s 可以分割为 “RL”、“LLLRRR”、“LR” ,每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’ 。
示例 3:
输入:s = “LLLLRRRR”
输出:1
解释:s 只能保持原样 “LLLLRRRR”.
示例 4:
输入:s = “RLRRRLLRLL”
输出:2
解释:s 可以分割为 “RL”、“RRRLLRLL” ,每个子字符串中都包含相同数量的 ‘L’ 和 ‘R’ 。
代码:
下面展示一些 内联代码片
。
利用并理解好 s[i] == 'L' ? ++d : --d操作
// An highlighted block
int balancedStringSplit(char * s)
int ans = 0, d = 0;
for (int i = 0; s[i]; i++)
s[i] == 'L' ? ++d : --d;//若指针字符串所指的位置是L则d增加,否则d减少
if (d == 0)
++ans;
return ans;
1167. 两数之和 II - 输入有序数组
OJ链接: https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
题目:给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
代码:
方法一:二分查找
在数组中找到两个数,使得它们的和等于目标值,可以首先固定第一个数,然后寻找第二个数,第二个数等于
目标值减去第一个数的差。利用数组的有序性质,可以通过二分查找的方法寻找第二个数。为了避免重复寻找,
在寻找第二个数时,只在第一个数的右侧寻找。
// An highlighted block
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize)
int* ret = (int*)malloc(sizeof(int) * 2);
*returnSize = 2;
for (int i = 0; i < numbersSize; ++i)
int low = i + 1, high = numbersSize - 1;
while (low <= high)
int mid = (high - low) / 2 + low;
if (numbers[mid] == target - numbers[i])
ret[0] = i + 1, ret[1] = mid + 1;
return ret;
else if (numbers[mid] > target - numbers[i])
high = mid - 1;
else
low = mid + 1;
ret[0] = -1, ret[1] = -1;
return ret;
方法二:双指针
初始时两个指针分别指向第一个元素位置和最后一个元素的位置。每次计算两个指针指向的两个元素之和,并和
目标值比以上是关于数据结构与算法1(LeetCode)的主要内容,如果未能解决你的问题,请参考以下文章