已知两个已排序数组,将这两个数组合并为一个排序数组
void merge_sort_two_vec(vector<int>& sub_vec1, vector<int>& sub_vec2, vector<int>& vec) {
int i = 0;
int j = 0;
while (i < sub_vec1.size() && j < sub_vec2.size()) {
if (sub_vec1[i] <= sub_vec2[j]) {
vec.push_back(sub_vec1[i]);
i++;
}
else{
vec.push_back(sub_vec2[j]);
j++;
}
}
for (; i < sub_vec1.size(); i++) {
vec.push_back(sub_vec1[i]);
}
for (; j < sub_vec2.size(); j++) {
vec.push_back(sub_vec2[j]);
}
}
测试代码如下:
int main() {
int test1[] = { 2,5,8,20 };
int test2[] = { 1,3,5,7,30,50 };
vector<int> sub_vec1;
vector<int> sub_vec2;
vector<int> vec;
for (int i = 0; i < 4; i++) {
sub_vec1.push_back(test1[i]);
}
for (int i = 0; i < 6; i++) {
sub_vec2.push_back(test2[1]);
}
merge_sort_two_vec(sub_vec1, sub_vec2, vec);
for (int i = 0; i < vec.size(); i++) {
printf("[%d]", vec[i]);
}
printf("\n");
return 0;
}
再参考leetcode第21题合并两个有序链表的思路:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode temp_head(0); //设置临时头节点temp_head
ListNode *pre = &temp_head; //使用pre指针指向temp_head
while(l1 && l2){
if(l1->val < l2->val){ //l1和l2同时不为空时,对它们进行比较
pre->next = l1;
l1 = l1->next;
}
else{
pre->next = l2;
l2 = l2->next;
}
pre = pre->next;
}
if(l1){ //如果l1有剩余
pre->next = l1;
}
if(l2){ //如果l2有剩余
pre->next = l2;
}
return temp_head->next;
}
};
分治算法:
将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解后进行合并,就可以得到原问题的解
一般步骤:
1、分解,将要解决的问题划分成若干规模较小的同类问题;
2、求解,当子问题划分得足够小时,用简单的方法解决
3、合并,按原问题的要求,将子问题的解逐层合并构成原问题的解
归并排序复杂度分析:
设有n个元素,n个元素归并排序的时间为T(n)
总时间 = 分解时间 + 解决子问题时间 + 合并时间
分解时间:即对yu原来问题拆分为两个字子问题的时间,复杂度O(n)
解决子问题时间:解决两个子问题的时间2T(n/2)
合并时间:即对两个已排序数组归并的时间,复杂度O(n)
T(n) = 2T(n/2) + 2O(n) = O(n + 2n/2 + 4n/4 +……+n*1) = O(nlogn)
void merge_sort(vector<int>& vec){
if (vec.size() < 2) { //子问题足够小时,直接求解
return;
}
int mid = vec.size() / 2;
vector<int> sub_vec1;
vector<int> sub_vec2;
for (int i = 0; i < mid; i++) {
sub_vec1.push_back(vec[i]);
}
for (int i = mid; i < vec.size(); i++) {
sub_vec2.push_back(vec[i]);
}
merge_sort(sub_vec1); //对拆解后的两个子问题进行求解
merge_sort(sub_vec2);
vec.clear();
merge_sort_two_vec(sub_vec1, sub_vec2, vec); //合并,将子问题的解进行合并
}
测试程序如下:
int main() {
vector<int> vec;
int test[] = { 5,-4,-9,0,12,34,2,1,10,-10 };
for (int i = 0; i < 10; i++) {
vec.push_back(test[i]);
}
merge_sort(vec);
for (int i = 0; i < vec.size(); i++) {
printf("[%d] ", vec[i]);
}
printf("\n");
return 0;
}
**给定一个整数数组 nums,按要求返回一个新数组 counts。**数组 counts 有该性质: counts[i] 的值是 nums[i]右侧小于nums[i]的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.
思考:
最暴力的方法,即对每个元素扫描其右侧比它小的数,累加个数。假设数组元素个数为N,算法复杂度O(N^2)(该算法在leetcode上无法通过,超时)
算法思路:在归并两排数组时,当需要将前一个数组元素的指针i指向的元素插入时,对应的count[i],即为指向后一个数组的指针j的值
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
vector<pair<int,int>> vec;
vector<int> count;
for (int i = 0; i < nums.size(); i++){
vec.push_back(make_pair(nums[i], i)); //将nums[i]与i绑定为pair<nums[i],i>
count.push_back(0);
}
merge_sort(vec, count);
return count;
}
private:
void merge_sort_two_vec(vector<pair<int,int>> &sub_vec1,
vector<pair<int,int>> &sub_vec2,
vector<pair<int,int>> &vec,
vector<int> &count){
int i = 0;
int j = 0;
while (i < sub_vec1.size() && j < sub_vec2.size()) {
if (sub_vec1[i].first <= sub_vec2[j].first) {
count[sub_vec1[i].second] += j;
vec.push_back(sub_vec1[i]);
i++;
}
else{
vec.push_back(sub_vec2[j]);
j++;
}
}
for (; i < sub_vec1.size(); i++) {
count[sub_vec1[i].second] += j;
vec.push_back(sub_vec1[i]);
}
for (; j < sub_vec2.size(); j++) {
vec.push_back(sub_vec2[j]);
}
}
void merge_sort(vector<pair<int,int>> &vec, vector<int> &count){
if(vec.size() < 2){ //子问题足够小时,直接求解
return;
}
int mid = vec.size() / 2;
vector<pair<int,int>> sub_vec1;
vector<pair<int,int>> sub_vec2;
for (int i = 0; i < mid; i++){ //对原问题进行分解
sub_vec1.push_back(vec[i]); //拆分为两个规模相同的数组
}
for (int i = mid; i < vec.size(); i++){
sub_vec2.push_back(vec[i]);
}
merge_sort(sub_vec1, count); //对拆解后的两个子问题进行求解排序
merge_sort(sub_vec2, count);
vec.clear();
//将子问题的解进行合并
merge_sort_two_vec(sub_vec1, sub_vec2, vec, count);
}
};
测试程序如下:
int main() {
vector<int> vec;
int test[] = { 5,-4,-9,0,12,34,2,1,10,-10 };
for (int i = 0; i < 10; i++) {
vec.push_back(test[i]);
}
Solution solve;
vector<int> result = solve.countSmaller(vec);
for (int i = 0; i < result.size(); i++) {
printf("[%d] ", result[i]);
}
printf("\n");
return 0;
}
思考:将元素按照原数组逆置后的顺序插入到二叉查找树中,如何在元素插入时,计算已有多少个元素比当前插入元素小
算法思路:
设置变量count_small = 0, 记录在插入过程中,有多少个元素比插入节点值小;
若待插入节点值小于等于当前节点node值,node->count++,递归将该节点插入到当前节点左子树
若待插入节点值大于当前节点的node值,count_small += node->count + 1(当前节点左子树数量+1),递归将该节点插入到当前节点右子树。
struct BSTNode{
int val;
int count;
BSTNode *left;
BSTNode *right;
BSTNode(int x) : val(x), left(NULL), right(NULL), count(0) {}
};
//在插入节点时,当待插入节点insert_node小于等于当前node时count++
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
std::vector<int> result; //最终逆序数数组
std::vector<BSTNode*> node_vec; //创建的二叉树查找树节点池
std::vector<int> count; //从后向前插入过程中,比当前节点值小的count_small数组
for (int i = nums.size() - 1; i >= 0; i--){
node_vec.push_back(new BSTNode(nums[i]));
}
count.push_back(0); //第一个节点count_small = 0
for (int i = 1; i < node_vec.size(); i++){ //将第2到第n个节点插入到以第一个节点为根的二叉排序树中,在插入过程中计算每个节点的count_small
int count_small = 0;
BST_insert(node_vec[0], node_vec[i], count_small);
count.push_back(count_small);
}
for (int i = node_vec.size() - 1; i >= 0; i--){
delete node_vec[i];
result.push_back(count[i]);
}
return result;
}
private:
void BST_insert(BSTNode* node, BSTNode* insert_node, int &count_small) {
if (insert_node->val <= node->val) { //count_small二叉排序树中比insert_node值小的节点个数
node->count++; //若是出现相等的情况则插入左边
if (node->left) {
BST_insert(node->left, insert_node, count_small);
}
else {
node->left = insert_node;
}
}
else {
count_small += node->count + 1;
if (node->right) {
BST_insert(node->right, insert_node, count_small);
}
else {
node->right = insert_node;
}
}
}
};