(更新完毕后会以题型划分)
1.链表的倒数第k个元素
(剑指Offer22)
- 思路1:二次遍历:第一次统计链表长度 第二次根据运算得到最终位置(效率低)
- 思路2:一次遍历双指针:快指针和慢指针跨度始终为k,使用一个计数器记录快指针走出的步数,到达k之后慢指针开始走,快指针到达链表尾部时慢指针也到达了需要结点。
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
//双指针法
if(!head)
return head;
int road=0;//记录前驱指针走了几步
ListNode *slow=head;
while(head){
head=head->next;
road++;
if(road>k)
slow=slow->next;
}
return slow;
}
};
2.求数组中的第K大数字
(leetcode 剑指Offer2:76)
- 思路1:先排序,再计算得知,但是一般的面试都不会要求这么做
- 思路2:这里使用堆排序
maxHeapify 函数实现了将指定节点 i 的子树构建成最大堆的操作。参数 a 是待构建的堆, heapSize 是堆的大小, l 和
r 分别表示节点 i 的左右孩子。
largest记录当前节点及其子节点中值最大的节点的下标,然后交换该节点和根节点,递归地调整交换后新的根节点,以保证堆的性质。buildMaxHeap 函数用于初始化堆。从堆的中间节点向根节点遍历,每个节点调用 maxHeapify函数,以保证每个节点的子树都是最大堆。
findKthLargest 函数首先使用 buildMaxHeap函数初始化整个堆,然后从堆的最后一个节点开始向前遍历,每次将根节点和当前节点交换,并且缩小堆的大小,最后将第k大元素返回。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int HeapSize=nums.size();
InitHeap(nums,HeapSize); //先初始化一下堆
for(int i=nums.size() - 1;i >= nums.size() - k + 1; --i){
swap(nums[0], nums[i]);
--HeapSize;
ModifyHeap(nums,0,HeapSize);
}
return nums[0];
}
//调整堆
void ModifyHeap(vector<int>& a,int i,int HeapSize){
//i用来表示当前处理的元素,
int lchild=i*2+1;
int rchild=i*2+2;
int largeNum=i; //largeNum用来排序过程中的最大元素
//对于当前元素,开始与左右孩子比较
if(lchild<HeapSize&&a[lchild] > a[largeNum]){
largeNum=lchild;
}
if(rchild<HeapSize&&a[rchild] > a[largeNum]){
largeNum=rchild;
}
if(largeNum!=i){
//如果最大元素不在当前元素
swap(a[largeNum],a[i]);
ModifyHeap(a,largeNum,HeapSize);
}
}
//初始化堆
void InitHeap(vector<int>& a,int HeapSize){
for(int i=HeapSize/2;i>=00;--i){
ModifyHeap(a,i,HeapSize);//为每个父节点构建堆
}
}
};
3.最长连续递增(子)序列
(leetcode 674)
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], …, nums[r - 1], nums[r]] 就是连续递增子序列。
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
输入:nums = [2,2,2,2,2]
输出:1
解释:最长连续递增序列是 [2], 长度为1。
这道题是要求连续的子数列,如果是非连续就难一点了,可能需要动态规划
这道题一次遍历就可以解决
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int maxLen = 0;
int n = nums.size();
int start = 0;
for (int i = 0; i < n; i++) {
if (i > 0 && nums[i] <= nums[i - 1]) {
start = i;
}
maxLen = max(maxLen, i - start + 1);
}
return maxLen;
}
};
4.原地判断回文链表
(leetcode 234)
- 思路:这道题的核心思路是将链表分成两个部分,将后半部分逆置,然后逐字比较,这个思想非常重要,我们后面有道题也会用到这个
class Solution {
public:
bool isPalindrome(ListNode* head) {
if (!head) return true;
ListNode *slow = head;
ListNode *fast = head;
while (fast->next != NULL && fast->next->next != NULL) {
fast = fast->next->next;
slow = slow->next;
}
ListNode *cur = NULL;
if (slow->next) {
cur = slow->next;
} else {
cur = slow;
}
ListNode *pre = NULL;
ListNode *preMove = NULL;
while (cur) {
preMove = cur->next;
cur->next = pre;
pre = cur;
cur = preMove;
}
while (head && pre) {
if (head->val != pre->val) {
return false;
}
head = head->next;
pre = pre->next;
}
return true;
}
};
5.顺时针90度旋转矩阵
(LeetCode 面试题 01.07. 旋转矩阵)
给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。
不占用额外内存空间能否做到?
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
思路:常规方法是找到对应元素的数学规律,但这种方法在考场上不好想到,还容易产生数组越界,这里提供一个简便算法:先按照行对称翻转,然后再让元素按对角线翻转
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
6.旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为
[1,2,3,4,5] 的一次旋转,该数组的最小值为 1。
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
- 思路:这道题是利用二分查找以及当前元素和Nums[mid]的关系
class Solution {
public:
int minArray(vector<int>& numbers) {
int end=numbers.size()-1;
int high=end;
int low=0;
while(low<high){
int mid=low+(high-low)/2; //防止溢出
//情况一 在左边
if(numbers[mid]>numbers[high]){
//说明在左边
low=mid+1;
continue;
}
else if(numbers[mid]<numbers[high]){
high=mid;
continue;
}
else {
high--;
}
}
return numbers[low];
}
};
7.斐波那契数列递归非递归
// 定义一个函数,用于计算第n个斐波那契数 递归
int fib(int n) {
// 如果n为0或1,直接返回n
if (n == 0 || n == 1) {
return n;
}
// 否则,返回前两个斐波那契数之和
else {
return fib(n - 1) + fib(n - 2);
}
}
/ 定义一个函数,用于计算第n个斐波那契数 非递归
int fib(int n) {
// 如果n为0或1,直接返回n
if (n == 0 || n == 1) {
return n;
}
// 否则,使用两个变量来存储前两个斐波那契数,并用一个循环来更新它们
else {
int a = 0; // 存储第n-2个斐波那契数
int b = 1; // 存储第n-1个斐波那契数
int c; // 存储第n个斐波那契数
for (int i = 2; i <= n; i++) {
// 循环从第二个开始到第n个结束
c = a + b; // 计算第i个斐波那契数为前两个之和
a = b; // 更新a为b
b = c; // 更新b为c
}
return c; // 返回第n个斐波那契数
}
}