一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
剑指 Offer 51. 数组中的逆序对
思路
(归并排序) O(nlogn)
归并排序模板:
const int maxn = 1e5 + 10;
int q[maxn], tmp[maxn];
void merge_sort(int q[], int l, int r)
{
if (l >= r) return; //如果只有一个数字或没有数字,则无需排序
int mid = (l + r ) / 2;
merge_sort(q, l, mid); //分解左序列
merge_sort(q, mid + 1, r); //分解右序列
int k = l, i = l, j = mid + 1;
while (i <= mid && j <= r) //合并
{
if (q[i] <= q[j]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
}
while (i <= mid) tmp[k++] = q[i++]; //复制左边子序列剩余
while (j <= r) tmp[k++] = q[j++]; //复制右边子序列剩余
for (int i = l; i <= r; i++) q[i] = tmp[i];
}
在归并排序的合并操作中,我们假设左右两个区间元素为:
左边:{3 4 7 9} 右边:{1 5 8 10}
那么合并操作的第一步就是比较3和1,然后将1取出来放到辅助数组中,这个时候我们发现,右边的区间如果是当前比较的较小值,那么其会与左边剩余的数字产生逆序关系,也就是说1和3、4、7、9都产生了逆序关系,因此我们可以一下子统计出有4对逆序对。接下来3,4取下来放到辅助数组后,5与左边剩下的7、9产生了逆序关系,我们可以统计出2对。依此类推,8与9产生1对,那么总共有4 + 2 + 1对。这样统计的效率就会大大提高,便可较好地解决逆序对问题。
而在算法的实现中,我们只需略微修改原有归并排序,当右边序列的元素为较小值时,就统计其产生的逆序对数量,即可完成逆序对的统计。
时间复杂度分析: 归并排序的时间复杂度为O(nlogn)。
c++代码
class Solution {
public:
int reversePairs(vector<int>& nums) {
return merge(nums, 0, nums.size() - 1);
}
int merge(vector<int>&nums, int l, int r){
if(l >= r) return 0; //序列中只有一个数
int mid = l + r>> 1;
int res = merge(nums, l, mid) + merge(nums, mid + 1, r);
vector<int> tmp;
int i = l, j = mid + 1;
while(i <= mid && j <= r){
if(nums[i] <= nums[j]) tmp.push_back(nums[i++]);
else{
res += mid - i + 1;
tmp.push_back(nums[j++]);
}
}
while(i <= mid) tmp.push_back(nums[i++]);
while(j <= r) tmp.push_back(nums[j++]);
int k = l;
for(int x : tmp) nums[k++] = x;
return res;
}
};
剑指 Offer 52. 两个链表的第一个公共节点
(链表,指针扫描) O(n)
这题的思路很巧妙,我们先给出做法,再介绍原理。
算法步骤:
-
用两个指针分别从两个链表头部开始扫描,每次分别走一步;
-
如果指针走到
null,则从另一个链表头部开始走; -
当两个指针相同时:
- 如果指针不是
null,则指针位置就是相遇点; - 如果指针是
null,则两个链表不相交;
- 如果指针不是
此题我们画图讲解,一目了然:
1、两个链表不相交:
a,b 分别代表两个链表的长度,则两个指针分别走 a + b 步后都变成 null。
2 、两个链表相交:
则两个指针分别走 a + b + c 步后在两链表交汇处相遇。
时间复杂度分析: 每个指针走的长度不大于两个链表的总长度,所以时间复杂度是O(n)。
c++代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
auto pA = headA, pB = headB;
while(pA != pB) {
if(pA) pA = pA->next;
else pA = headB;
if(pB) pB = pB->next;
else pB = headA;
}
return pA;
}
};
剑指 Offer 53 - I. 在排序数组中查找数字 I
思路
c++代码
class Solution {
public:
int search(vector<int>& nums, int target) {
if(!nums.size()) return 0;
int l = 0, r = nums.size() - 1;
while(l < r) //查找target的开始位置
{
int mid = (l + r) / 2;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if(nums[r] != target) return 0 ; //查找失败
int begin = r; //记录开始位置
l = 0, r = nums.size() - 1;
while(l < r) //查找tatget的结束位置
{
int mid = (l + r + 1) / 2;
if(nums[mid] <= target) l = mid;
else r = mid - 1;
}
int end = r; //记录结束位置
return end - begin + 1;
}
};