算法珠玑(1)线性表--算法题解<1>数组

算法珠玑(1)线性表–算法题解

1.数组

(1)Remove Duplicates from Sorted Array

问题

给定一个有序数组,除去其中的重复元素并返回之后的数组大小。要求:不能给新数组分配额外空间(也就是不能额外数组来作为辅助,这样会占用额外的空间)。

比如给定一个数组 A = [ 1 , 1 , 2 ] A=[1,1,2] A=[1,1,2],你的函数应该返回2且数组 A A A变为 [ 1 , 2 ] [1,2] [1,2]

书中给出了标准解法,返回值没问题,但光是看代码就能看出这个解法是不和要求的,因为书中的解法没有重要的一步:删除数组A多余的元素。所以我给出我的在其基础上的改进的版本:

#include<vector>
#include<iostream>
using namespace std;

int removeDuplicates(vector<int>& nums) {
	if (nums.empty())return 0;
	int index = 1;
	for (int i = 1; i < nums.size(); ++i) {
		if (nums[i] != nums[index - 1])
			nums[index++] = nums[i];
	}
	nums.erase(nums.begin() + index, nums.end());
	return index;
}
int main() {
	vector<int>A = { 1,1,2,2,3,4,5,6,7,7,8 };
	int ind = removeDuplicates(A);
	cout << "A=[";
	for (auto c : A)
		cout << c<<((c==A[ind-1])?"":",");
	cout << "] " << "and A.size()=" << ind;
}

(2)Remove Duplicates from Sorted Array -2

问题

接着上一个问题,这个问题中将允许元素最多重复两次,也就是说一个数组中一个元素重复两次没问题,但如果一个元素重复的次数超过两次则删除多余部分。

比如,若给定输入数组 A = [ 1 , 1 , 1 , 2 , 2 , 3 ] A=[1,1,1,2,2,3] A=[1,1,1,2,2,3],应该输出5,且 A A A变为 [ 1 , 1 , 2 , 2 , 3 ] [1,1,2,2,3] [1,1,2,2,3]

很简单,在上一个代码基础上稍微修改一下就行了

#include<vector>
#include<iostream>
using namespace std;

int removeDuplicates(vector<int>& nums) {
	if (nums.size()<=2)return nums.size();
	int index = 2;
	for (int i = 2; i < nums.size(); ++i) {
		if (nums[i] != nums[index - 2])
			nums[index++] = nums[i];
	}
	nums.erase(nums.begin() + index, nums.end());
	return index;
}
int main() {
	vector<int>A = { 1,1,1,1,2,2,2,2,3,3,4,5,6,7,7,8 };
	int ind = removeDuplicates(A);
	cout << "A=[";
	for (auto c : A)
		cout << c<<((c==A[ind-1])?"":",");
	cout << "] " << "and A.size()=" << ind;
}

(3)Longest Consecutive Sequence

问题

给定一个无序数组,寻找其中最大的连续元素序列。要求算法的复杂度在 O ( n ) O(n) O(n)内。

如,给定数组 [ 100 , 4 , 200 , 1 , 3 , 2 ] [100,4,200,1,3,2] [100,4,200,1,3,2],最大连续元素序列为 [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4],返回4。

由于复杂度的要求,不能先排序。这样的话可以用哈希表。

这里我们用到的unordered_set是一种关联容器,用哈希组织的(无序的)set,set在元素上的特点和vector的区别在于它的元素不会重复。关于这种容器的更多信息可以参考《C++primer》。

#include<vector>
#include<iostream>
#include<unordered_set>
using namespace std;

int longest_c_s(const vector<int>& nums) {
	unordered_set<int>my_set;
	for (auto i : nums)my_set.insert(i);
	int longest = 0;
	for (auto i : nums) {
		int length = 1;
		for (int j = i - 1; my_set.find(j)!= my_set.end(); --j) {
			my_set.erase(j);
			++length;
		}
		for (int j = i + 1; my_set.find(j) != my_set.end(); ++j) {
			my_set.erase(j);
			++length;
		}
		longest = longest < length ? length : longest;
	}
	return longest;
}
int main() {
	vector<int>A = { 100,4,200,1,3,2 };
	int ind = longest_c_s(A);
	cout << ind << endl;
}

(3)Two Sum

问题

给定一个数组和一个目标数值,寻找数组中的两个元素他们的和正好等于目标元素,返回这两个元素的下标。

如:输入数组{2,7,11,15},target=9;输出:index1=1,index2=2。

这个问题有三个解法思路:一种自然是暴力解法,这种最直接,但它的复杂度为 O ( n 2 ) O(n^2) O(n2),不是我们需要的最佳解法。一种是用哈希表,复杂度为 O ( n ) O(n) O(n),这个思路是没问题的。还有一个思路是先排序再左右夹逼,排序复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn),左右夹逼复杂度 O ( n ) O(n) O(n),但问题是我们要求原数组两个元素对应的下标而不是元素,排序以后的元素对应的下标不是我们要找的,所以这个问题要行得通就必须解决排序前后下标的对应问题。书上毫无疑问是采用了第二种解法。我们们这里三种解法都试试。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
//解法一(暴力求解)
vector<int>twosum_1(vector<int>& nums, int target) {
	vector<int>index;
	for (int i = 0; i < nums.size()-1; ++i) {
		for (int j = i + 1; j < nums.size(); ++j) {
			if (nums[i] + nums[j] == target) {
				index.push_back(i + 1);
				index.push_back(j + 1);
				return index;
			}
		}
	}
}
//解法二(哈希表求解)
vector<int>twosum_2(vector<int>& nums, int target) {
	unordered_map<int, int>my_map;
	vector<int>index;
	for (int i = 0; i < nums.size(); ++i)
		my_map[nums[i]] = i;
	for (int i = 0; i < nums.size(); ++i) {
		auto iter = my_map.find(target - nums[i]);
		if (iter != my_map.end()) {
			index.push_back(i + 1);
			index.push_back(iter->second + 1);
			return index;
		}
	}
}
//解法三(排序后左右夹逼求解)
vector<int>twosum_3(vector<int>& nums, int target) {
	vector<int>index;
	unordered_map<int, int>my_map;
	for (int i = 0; i < nums.size(); ++i)
		my_map[nums[i]] = i;
	sort(nums.begin(),nums.end());
	int j = nums.size() - 1, i = 0;
	while (i < j) {
		if (nums[i] + nums[j] > target)--j;
		else if (nums[i] + nums[j] < target)++i;
		else{
			index.push_back(my_map[nums[i]]+1);
			index.push_back(my_map[nums[j]]+1);
			sort(index.begin(), index.end());
			return index;
		}
	}
}
int main() {
	vector<int>A = { 100,4,200,1,3,2 };
	vector<int>t1 = twosum_1(A, 7), t2 = twosum_2(A, 7),t3=twosum_3(A,7);
	cout <<"解法1:index1="<< t1[0]<<", index2="<<t1[1] << endl;
	cout << "解法2:index1=" << t2[0] << ", index2=" << t2[1] << endl;
	cout << "解法3:index1=" << t3[0] << ", index2=" << t3[1] << endl;
}

(4)3Sum

问题

给定一个n个元素的数组,找出其中所有的和为0的三元素组合。

要求:输出的三元素组合(a,b,c)的顺序不能按从大到小的顺序排( a ≤ b ≤ c a\le b \le c abc),而且不能重复输出相同的数组。

比如,输入数组s={-1,0,1,2,-1,-4},输出:(-1,0,1),(-1,-1,2)

这个问题也是可以暴力解法的,但直接暴力解法的话复杂度应该是 O ( n 3 ) O(n^3) O(n3),更好的 做法是先排序再左右夹逼,复杂度为 O ( n 2 ) O(n^2) O(n2)

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

vector<vector<int>>threeSum(vector<int>& nums) {
	vector<vector<int>>result;
	if (nums.size() < 3)return result;
	sort(nums.begin(), nums.end());
	const int target = 0;
	auto last = nums.end();
	for (auto i = nums.begin(); i < last - 2; ++i) {
		if (i < nums.begin() && *i == *(i - 1))continue;
		auto j = i + 1;
		auto k = last - 1;
		while (j < k) {
			if (*i + *j + *k < target){
				++j;
				while(*j==*(j-1)&&j<k)++j;
				}
			else if(*i+*j+*k>target){
				--k;
				while(*k==*(k+1)&&j<k)--k;
			}
			else {
				if (result.size() == 0) {
					result.push_back({ *i,*j,*k });
				}
				else {
					bool dup = false;
					for (auto c : result) {
						if (c[0] == *i && c[1] == *j && c[2] == *k)
							dup = true;
					}
					if(!dup)
						result.push_back({ *i,*j,*k });
				}
				++j;
				--k;
				while (*j == *(j - 1) && j < k)++j;
				while (*k == *(k + 1) && j < k)--k;
			}
		}
	}
	return result;
}
int main() {
	vector<int>A = { -1,0,1,2,-1,-4};
	vector<vector<int>>res = threeSum(A);
	for (auto c : res) {
		for (auto cc : c) {
			cout << cc<<"  ";
		}
		cout << endl;
	}
}

(5)3Sum Closest

问题:

给定一个n个元素的数组,寻找其中的三个元素使得他们的和最接近给定的数值,返回三个元素之和。

比如:给定S={-1,2,1,-4},target=1;返回值为2。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

int threeSumClosest(vector<int>& A, int target) {
	sort(A.begin(), A.end());
	int result = 0, min_gap = INT_MAX;
	//接下来既可以用迭代器也可以用下标运算,但还是下标运算
	//不容易出错,同时也更具有易读性
	for (int x = 0; x != A.size() - 2; ++x) {
		int y = x + 1, z = A.size() - 1;
		while (y < z) {
			int gap = abs(A[x] + A[y] + A[z] - target);
			if (gap < min_gap) {
				result = A[x] + A[y] + A[z];
				min_gap = gap;
				}
			if (A[x] + A[y] + A[z] < target)++y;
			else --z;
		}
	}
	return result;
}
int main() {
	vector<int>A = { -1,2,1,-4};
	int res = threeSumClosest(A, 1);
	cout << res << endl;
}

(6)4Sum

问题:

给定一个数组和一个数值,寻找数组中的和为给定数值的所有的四元素组合。

要求:输出的结果的四元素必须按从小到大的顺序排列,同时结果的四元素组合不能重复。

这个问题有两种方法,一种是先排序再左右夹逼,复杂度 O ( n 3 ) O(n^3) O(n3),一种是用hashmap先缓存两个数的和,复杂度平均 O ( n 2 ) O(n^2) O(n2),最坏 O ( n 4 ) O(n^4) O(n4)

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
//解法一
vector<vector<int>>fourSum_1(vector<int>& A, int target) {
	vector<vector<int>>result;
	if (A.size() < 4)return result;
	sort(A.begin(), A.end());
	for (int a = 0; a != A.size()-3; ++a) {
		for (int b = a + 1; b != A.size() - 2; ++b) {
			int c = b + 1, d = A.size() - 1;
			while (c<d)
			{
				if (A[a] + A[b] + A[c] + A[d] > target)--d;
				else if (A[a] + A[b] + A[c] + A[d] < target) ++c;
				else {
					result.push_back({ A[a],A[b],A[c],A[d] });
					++c;
					--d;
				}
			}
		}
	}
	return result;
}
//解法二
vector<vector<int>>fourSum_2(vector<int>& A, int target) {
	vector<vector<int>>result;
	if (A.size() < 4)return result;
	sort(A.begin(), A.end());
	unordered_map<int, vector<pair<int, int>>>cache;
	for (int x = 0; x < A.size() - 1; ++x)
		for (int y = x + 1; y < A.size(); ++y)
			cache[A[x] + A[y]].push_back(pair<int, int>(x, y));
	for (int x = 0; x < A.size() - 1; ++x) {
		for (int y = x + 1; y < A.size(); ++y) {
			int key = target - A[x] - A[y];
			if (cache.find(key) != cache.end()) {
				vector<pair<int,int>>vi = cache[key];
				for (auto c : vi) {
					if (y < c.first) {
						result.push_back({ A[x], A[y], A[c.first], A[c.second] });
					}
				}
			}
		}
	}
	return result;
}
int main() {
	vector<int>A = {1,0,-1,0,-2,2};
	vector<vector<int>>res_1 = fourSum_1(A, 0);
	vector<vector<int>>res_2= fourSum_2(A, 0);
	cout << "解法一" << endl;
	for(auto c:res_1){
		for (auto cc : c) {
			cout << cc << "--";
		}
		cout << endl;
	}
	cout << "解法二" << endl;
	for (auto c : res_2) {
		for (auto cc : c) {
			cout << cc << "--";
		}
		cout << endl;
	}
}

(7)Remove Element

问题:

给定一个数组和一个数值,删除数组中所有与给定数组相等的元素,返回删除之后数组的大小。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std
int remove_element(vector<int>& A, int target) {
	int index = 0;
	for (auto c:A)
		if (c != target)
			A[index++] = c;
	A.erase(A.begin() + index, A.end());
	return index;
}
int main() {
	vector<int>A = {1,0,-1,0,-2,2};
	int len = remove_element(A, 0);
	cout << len << endl;
	for (auto c : A)
		cout << c << endl;
}

(8)Move Zeroes

问题:给定一个数组,将数组中的所有为0的元素移动到末尾,保持其他元素的相对位置不变。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
void move_zeroes(vector<int>& A) {
	int index = 0;
	for (auto c:A)
		if (c != 0)
			A[index++] = c;
	for (int x = index; x < A.size(); ++x)
		A[x] = 0;
}
int main() {
	vector<int>A = {1,0,-1,0,-2,2};
	move_zeroes(A);
	for (auto c : A)
		cout << c << endl;
}

(9)Next Permutation

问题:

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
The replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

这个问题的原文如上,我也是看了好久再结合所给的示例和书上接下来的分析才大概有自己的理解。

这个问题本质上是要改变一个给定数组的顺序,怎么改变呢?一个数组的所有元素按照其固有的顺序从左至右的排列可以代表一个数值,我们要通过改变某些元素的顺序使得数组对应的数值变得比原先更大一点点。如果原数组对应的数值无法再变大了(元素顺序为从大到小),就使得原数组按从小到大顺序排列。如:
1 , 2 , 3 → 1 , 3 , 2 3 , 2 , 1 → 1 , 2 , 3 1 , 1 , 5 → 1 , 5 , 1 1,2,3 \to1,3,2\\ 3,2,1\to1,2,3\\ 1,1,5\to1,5,1 1,2,31,3,23,2,11,2,31,1,51,5,1
具体分析参考:

https://www.bookstack.cn/read/algorithm-essentials-java/12.md,解题思路要靠自己去领悟。

简单说明一下其思路的话,就是首先从右往左选定第一个违反升序的前一个元素,再选定该元素右边的比它稍微大一点的元素,将这两个元素互换位置,然后对第一个选定元素位置之后的所有元素升序排列。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
void next_permutation(vector<int>& A) {
	int p = A.size() - 2;
	while (p > -1 && A[p] >= A[p + 1])--p;
	if (p == -1) {
		reverse(A.begin(), A.end());
		return;
	}
	int c = A.size() - 1;
	while (c > 0 && A[c] <= A[p])--c;
	swap(A[p], A[c]);
	reverse(A.begin()+p+1, A.end());
}
int main() {
	vector<int>A = {6,8,7,4,3,2};
	next_permutation(A);
	for (auto c : A)
		cout << c << endl;
}

(10)Permutation Sequence

问题:

集合 [ 1 , 2 , 3 , . . . , n ] [1,2,3,...,n] [1,2,3,...,n]一共包含 n ! n! n!种不同的排列,我们可以列出所有的排列并给每个排列编号。如n=3时有如下编号的排列
1. " 123 " 2. " 132 " 3. " 213 " 4. " 231 " 5. " 312 " 6. " 321 " 1."123"\\ 2. "132"\\ 3. "213"\\ 4. "231"\\ 5. "312"\\ 6. "321" 1."123"2."132"3."213"4."231"5."312"6."321"
给定数字n和数字k,返回第k个排列。(n在1到9之间)

我们马上想到可以利用上一题的解法,只要对升序的数组做k-1此next_permutation就能得到想要的结果了。但这个方法时间复杂度为 O ( k n ) O(kn) O(kn),它会接连求出前k个排列,所以会有点啰嗦,我们希望有直接求第k个排列的方法。

可以利用康托编码的思路。假设第k个排列为 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,我们要求 a 1 a_1 a1的数组,注意到除去 a 1 a_1 a1还有n-1个元素,也就有 ( n − 1 ) ! (n-1)! (n1)!种排列了,所以有 a 1 = k ( n − 1 ) ! a_1=\frac{k}{(n-1)!} a1=(n1)!k(取整数部分)。(比如n=3时,看到上面列出来的所有排列,k=1,2时第一个元素是1,。。。)同理可求得其他元素:
k 2 = k % ( n − 1 ) ! a 2 = k 2 ( n − 2 ) ! … k_2=k\%(n-1)!\\ a_2=\frac{k_2}{(n-2)!}\\ \dots k2=k%(n1)!a2=(n2)!k2
时间复杂度为O(n)

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
unsigned int factorial_table[] = {
1,1,2,6,24,120,720,5040,40320,362880 };
using namespace std;

vector<int>permutation_sequence(int n, int k) {
	vector<int>result, s;
	for (int i = 1; i <= n; ++i)s.push_back(i);
	int a1 = (k-1) / factorial_table[n-1];
	result.push_back(s[a1]);
	s.erase(s.begin() + a1);
	for (int x = n - 1; x > 0; --x) {
		int a = (k-1)%factorial_table[x];
		int r = a / factorial_table[x - 1];
		result.push_back(s[r]);
		s.erase(s.begin() + r);
	}
	return result;
}
int main() {
	vector<int>A = permutation_sequence(5,5);
	for (auto c : A)
		cout << c << endl;
}

(11)Valid Sudoku

问题:

确定一个数独是不是有效的(有没有解)。数独游戏我们都懂,所以就不多说了。如图为一个有效的数独游戏:

img

空白方格我们可以用字符"."表示。

由于自己实践的话就需要自己手动输入数独,这样的二维数组,比较麻烦所以我直接把书上的标准答案放上来了,基本思路很简单,就是依次检测各行各列和各个九宫格看他们是否有重复的数字,有的话数独无效。书上的标准答案非常简洁精炼,还是有点水平的。自己可以体会一下。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
// Valid Sudoku
// 时间复杂度O(n^2),空间复杂度O(1)
class Solution {
public:
    bool isValidSudoku(const vector<vector<char>>& board) {
        bool used[9];
        for (int i = 0; i < 9; ++i) {
            fill(used, used + 9, false);
            for (int j = 0; j < 9; ++j) // 检查行
                if (!check(board[i][j], used))
                    return false;
            fill(used, used + 9, false);
            for (int j = 0; j < 9; ++j) // 检查列
                if (!check(board[j][i], used))
                    return false;
        }
        for (int r = 0; r < 3; ++r) // 检查 9 个子格子
            for (int c = 0; c < 3; ++c) {
                fill(used, used + 9, false);
                for (int i = r * 3; i < r * 3 + 3; ++i)
                    for (int j = c * 3; j < c * 3 + 3; ++j)
                        if (!check(board[i][j], used))
                            return false;
            }
        return true;
    }
    bool check(char ch, bool used[9]) {
        if (ch == '.') return true;
        if (used[ch - '1']) return false;
        return used[ch - '1'] = true;
    }
};

(12)Trapping Rain Water

问题:

给定n个非负整数代表一个地形的高度指数,求一场雨后该地形可能容纳的雨水量。如下图,给定数组[0,1,0,2,1,0,1,3,2,1,2,1],返回6。

Trapping Rain Water

思路很简单:对于每个柱子所能容纳的雨水为其左边的最大值与右边的最大值中的最小值减去柱子的高度: min ⁡ ( m a x l e f t , m a x r i g h t ) − h e i g h t \min (max_left,max_right)-height min(maxleft,maxright)height

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
int trapping(vector<int>& A) {
	vector<int>l, r;
	l.push_back(A[0]);
	r.push_back(A[A.size() - 1]);
	for (int i = 1; i < A.size(); ++i) {
		int lmax = A[i]>l[i-1]?A[i]:l[i-1];
		int rmax = A[A.size() - i - 1] > r[i - 1] ? A[A.size() - i - 1] : r[i - 1];
		l.push_back(lmax);
		r.push_back(rmax);
	}
	reverse(r.begin(), r.end());
	int result = 0;
	for (int i = 0; i < A.size(); ++i) {
		result += (r[i] < l[i] ? r[i] : l[i]) - A[i];
	}
	return result;
}
int main() {
	vector<int>A = { 0,1,0,2,1,0,1,3,2,1,2,1 };
	int res = trapping(A);
	cout << res << endl;
}

(13)Rotate Image

问题:

给定一个 n × n n\times n n×n的矩阵代表一个图像,求顺时针旋转图像90度。

顺时针旋转90度等价于先沿着副对角线翻转再沿着水平中线翻转,或者沿着水平中线翻转然后沿着主对角线翻转。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

void rotate(vector<vector<int>>& A) {
	for (int i = 0; i < A.size() - 1; ++i)
		for (int j = 0; j < A.size() - 1 - i; ++j)
			swap(A[i][j], A[A.size() - 1 - j][A.size() - 1 - i]);
	for (int i = 0; i < A.size()/2; ++i)
		for (int j = 0; j < A.size(); ++j)
			swap(A[i][j], A[A.size() - 1 - i][j]);
}
int main() {
	vector<vector<int>>A = { {1,2,3,4} ,{5,6,7,8},{9,10,11,12},{13,14,15,16} };
	rotate(A);
	for (auto c : A) {
		for (auto cc : c)
			cout << cc << "  ";
		cout << endl;
	}
}

(14)Plus One

问题:

给定一个数组的元素分别对应一个数字的各位上的数,以此求这个数加上一个给定的数字的结果以数组的形式输出。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

void pluss(vector<int>& A,int c) {
	for (int i = A.size() - 1; i >= 0; --i) {
		A[i] +=c;
		c = A[i] / 10;
		A[i] %= 10;
	}
	while (c > 0) {
		A.insert(A.begin(), c % 10);
		c /= 10;
	}
}
int main() {
	vector<int>A = { 1,2,3,4,5,6 };
	pluss(A,211234567);
	for (auto c : A)
		cout << c << endl;
	cout << 123456 + 21134567 << endl;
}

(14)Climbing Stairs

问题:假如你正在爬有n个阶的楼梯,你每一步要么爬一阶要么爬两阶,问一共有多少种不同的方式爬到楼顶。

若n阶楼梯一共有f(n)种方式,可以得出递推关系式: f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2),这是一个斐波那契数列。

所以我们有三种方法求:递归,迭代,数学公式。

斐波那契数列的通项公式为:
a n = 1 5 [ ( ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] a_n=\frac{1}{\sqrt{5}}\left[\left(\frac{(1+\sqrt{5}}{2}\right)^n-\left(\frac{1-\sqrt{5}}{2}\right) ^n\right] an=5 1[(2(1+5 )n(215 )n]

很简单,这里我们直接用迭代来求。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
int climbing(int n) {
	int prr = 0,pr=1,res=0;
	for (int i = 0; i < n; ++i) {
		res = pr + prr;
		prr = pr;
		pr = res;
	}
	return res;
}

int main() {
	for (int i = 1; i < 10; ++i)
		cout << climbing(i) << endl;
}

(15)Set Matrix Zeroes

问题:

给定一个 m × n m\times n m×n的矩阵,若其中某个元素为0,则令该元素的同行同列元素都为0。

直接求解这个问题的话空间复杂度为O(mn),也就是需要较多的额外内存占用,不是一个好方法。

所以要求通过改进使空间复杂度在 O ( m + n ) O(m+n) O(m+n)内。

只需要两个额外的bool数组记录每行每列是否有为0的元素就可以了。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;
void zeros(vector<vector<int>>& A) {
	vector<bool>r, c;
	for (int i = 0; i < A.size(); ++i)r.push_back(false);
	for (int i = 0; i < A[0].size(); ++i)c.push_back(false);
	for (int i = 0; i < A.size();++i) {
		for (int j = 0; j < A[0].size();++j) {
			if (A[i][j] == 0) {
				r[i] = true;
				c[j] = true;
			}
		}
	}
	for (int i = 0; i < A.size(); ++i) {
		for (int j = 0; j < A[0].size(); ++j) {
			if (r[i] || c[j])
				A[i][j] = 0;
		}
	}
}
int main() {
	vector<vector<int>>A = { {1,2,3,0},{2,3,0,1},{1,2,3,4} };
	zeros(A);
	for (auto c : A) {
		for (auto cc : c)
			cout << cc << "  ";
		cout << endl;
	}
}

(16)Gas Station

问题:

在一个环形路线上一共有N个加油站,第i个加油站的油量为gas[i]。

你有一辆无线存储油量的车,已知从加油站i到加油站(i+1)耗油cost[i]。你从其中一个加油站出发且最初储存的油量是空的。

如果你能绕环形道路行驶一圈返回起始的加油站的指标,否则返回-1。

注:结果确保是独特的。

这个问题直接的解法是对每个点进行尝试,复杂度为 O ( N 2 ) O(N^2) O(N2),但有更好的方法,复杂度为 O ( N ) O(N) O(N)

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

int gas_station(vector<int>& gas, vector<int>& cost) {
	int total = 0, j = -1;
	for (int i = 0, sum = 0; i < gas.size(); ++i) {
		sum += gas[i] - cost[i];
		total+=gas[i] - cost[i];
		if (sum < 0) {
			j = i;
			sum = 0;
		}
	}
	return total >= 0 ? j + 1 : -1;
}
int main() {
	vector<int>gas = { 6,7,5,6,3,5 }, cost = { 5,7,7,4,5,4 };
	int res = gas_station(gas, cost);
	cout << res << endl;
}

(17)Candy

问题:

在一条直线上站着N个小孩,每个小孩被分配一个评定数值。

你需要给所有的小孩分配糖果满足:

  • 每个孩子至少要有一颗糖果
  • 相邻的孩子评定数值更高的分配更多的糖果,求你需要准备的最少的糖果数量
#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

int candy(vector<int>A) {
	vector<int>add = { 1 };
	for (int i = 1, inc = 1; i < A.size(); ++i) {
		inc= A[i] > A[i - 1] ? inc+1 : 1;
		add.push_back(inc);
	}
	for (int i = A.size() - 2, inc = 1; i >= 0; --i) {
		inc = A[i] > A[i + 1] ? inc + 1 : 1;
		add[i] = A[i] > A[i + 1] ? (inc > add[i] ? inc : add[i]) : add[i];
	}
	int sum = 0;
	for (auto c : add)
		sum += c;
	return sum;
}
int main() {
	vector<int>a = { 2,4,5,3,2,1,6,2,4,1 };
	int s = candy(a);
	cout << s << endl;
}

(18)Majority Element

问题:

给定一个n元素的数组,找出其中的多位数(出现次数大于 [ n / 2 ] [n/2] [n/2]的元素)。假定数组总是存在多位数。

最简单的解法是排序找出最长连续字串。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

int maj_e(vector<int>&A) {
	sort(A.begin(), A.end());
	int res = 0, tt = 0;;
	for (int i = 1,t=1; i < A.size(); ++i) {
		if (A[i] == A[i - 1])
			++t;
		else {
			if (tt < t) {
				tt = t;
				res = A[i - 1];
			}
			t = 1;
		}
	}
	return res;
}
int main() {
	vector<int>a = { 2,4,5,4,4,3,4,4 };
	int s = maj_e(a);
	cout << s << endl;
}

(19)Rotate Array

循环一个n元素的数组k次。

如数组[1,2,3,4,5,6,7],k=3,结果为[5,6,7,1,2,3,4]。

注:可以尽可能的寻找多种解法。

书上给出了三种方法,第三种方法感觉是比较好的方法。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
using namespace std;

void rotate_a(vector<int>& A,int k) {
	reverse(A.begin(), A.end() - k);
	reverse(A.end() - k, A.end());
	reverse(A.begin(), A.end());
}
int main() {
	vector<int>a = {1,2,3,4,5,6,7 };
	rotate_a(a, 3);
	for (auto c : a)
		cout << c << "  ";
	cout << endl;
}

(20)Contains Duplicate

问题:

给定一个数组,确定数组种是否包含重复元素。

两个方法:一个用HashSet,一个先排序再遍历数组。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
#include<set>
using namespace std;

bool isduplicate_1(vector<int>& A) {
	set<int>a;
	for (auto c : A)
		a.insert(a.begin(),c);
	return a.size() != A.size();
}
bool isduplicate_2(vector<int>& A) {
	sort(A.begin(), A.end());
	for (size_t i = 1; i < A.size(); ++i)
		if (A[i] == A[i - 1])
			return true;
	return false;
}

int main() {
	vector<int>A = {1,2,3,4,5,6,7,4 };
	cout <<"解法一:"<< isduplicate_1(A) <<",解法二:"<<isduplicate_2(A)<< endl;
}

(21)Contains Duplicate 2

问题;

给定一个数组和一个数k,如果数组中有重复元素且重复元素的下标i,j之间的距离小于等于k则返回true否则返回false。

用HashMap。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
#include<set>
#include<map>
using namespace std;

bool isd(vector<int>& A,int k) {
	map<int, int>a;
	for (int i = 0,ind=-1; i < A.size(); ++i) {
		if (a.find(A[i]) != a.end()){
			if (i - a[A[i]] <= k)
				return true;
		}
		else
			a[A[i]] = ++ind;
	}
	return false;
}
int main() {
	vector<int>A = {1,2,3,4,5,6,7,4 };
	cout <<isd(A,3)<< endl;
}

(22)Contains Duplicate 3

问题:

给定一个数组,寻找两个指标i和j使得对应的元素只差最大为t且i和j只差最大为k。若能找到则返回true否则返回false。

这个问题和上个问题相比只是增加了一个条件。由寻找相等的元素变为寻找数值相近的元素。

书上建议用二叉搜索数BST。这里我嫌麻烦就直接在上一题的代码的基础上稍微修改一下得到这一题的解法。虽然这样的时间复杂度可能会比较大。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
#include<set>
#include<map>
using namespace std;

bool isd(vector<int>& A,int k,int t) {
	map<int, int>a;
	for (int i = 0,ind=-1; i < A.size(); ++i) {
		for (int j = -t; j <= t; ++j) {
			if (a.find(A[i]+j) != a.end()){
				if (i - a[A[i]] <= k)
					return true;
		}
		else if(j==0)
			a[A[i]] = ++ind;
		}
		
	}
	return false;
}
int main() {
	vector<int>A = {1,2,3,4,5,6,7,4 };
	cout <<isd(A,3,1)<< endl;
}

(23)Product of Array Except Self

问题:

给定一个n个元素的数组,n>1,输出数组的第i个元素为给定数组的除第i个数组的所有元素的乘积。如给定的数组为[1,2,3,4],输出数组[24,12,8,6]。要求不能用除法且空间复杂度在O(n)内。

更近一步:能不能求解这个问题使得空间复杂度为常数。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
#include<set>
#include<map>
using namespace std;

vector<int> paes(vector<int>& A) {
	vector<int>a1 = { 1 }, a2{ 2 };
	for (int i = 0,v1=1,v2=1; i < A.size()-1; ++i) {
		v1 *= A[i];
		v2 *= A[A.size() - 1 - i];
		a1.push_back(v1);
		a2.push_back(v2);
	}
	for (int i = 0; i < A.size(); ++i)
		a1[i] *= a2[A.size() - 1 - i];
	return a1;
}
int main() {
	vector<int>A = {1,2,3,4 };
	vector<int>a = paes(A);
	for (auto c : a)
		cout << c << endl;
}

(24)Increasing Triplet Subsequence

问题:

给定一个未排序的数组a,确定是否存在递增的三元组,即是否存在i,j,k使得A[i]<A[j]<A[k],且i<j<k,存在的话返回true否则返回false。

#include<vector>
#include<iostream>
#include<unordered_map>
#include<algorithm>
#include<set>
#include<map>
using namespace std;

bool its(vector<int>& A) {
	for (size_t i = 1,min=A[0],min1=A[0]; i < A.size(); ++i) {
		if (A[i] <= min)
			min = A[i];
		else if (A[i]<=min1||i==1)
			min1 = A[i];
		else
			return true;
	}
	return false;
}
int main() {
	vector<int>A = {1,2,3,4};
	cout << its(A) << endl;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优化这段代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>图书购物车</title> <style> </style> <script src="js/vue.js"></script> </head> <body> <div id="demo"> <table border="1"> <tr> <td></td> <td>书籍名称</td> <td>出版日期</td> <td>价格</td> <td>购买数量</td> <td>操作</td> </tr> <tr> <td></td> <td>{{books1.name}}</td> <td>{{books1.date}}</td> <td>¥{{books1.price}}</td> <td><button @click="down(books1)">-</button>{{books4.count}}<button @click="up(books1)">+</button></td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books2.name}}</td> <td>{{books2.date}}</td> <td>¥{{books2.price}}</td> <td> <button @click="down(books2)">-</button>{{books4.count}}<button @click="up(books2)">+</button> </td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books3.name}}</td> <td>{{books3.date}}</td> <td>¥{{books3.price}}</td> <td> <button @click="down(books3)">-</button>{{books4.count}}<button @click="up(books3)">+</button> </td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books4.name}}</td> <td>{{books4.date}}</td> <td>¥{{books4.price}}</td> <td> <button @click="down(books4)">-</button>{{books4.count}}<button @click="up(books4)">+</button> </td> <td><button @click="del">移除</button></td> </tr> </table> <div>总价: ¥{{sum}}</div> </div> <!-- js部分 --> <script> const vm = new Vue({ el: "#demo", data: { books1: { name: '《算法导论》', date: '2006-9', price: 85.00, count: 1 }, books2: { name: '《UNIX编程艺术》', date: '2006-2', price: 59.00, count: 1 }, books3: { name: '《编程珠玑》', date: '2008-10', price: 39.00, count: 1 }, books4: { name: '《代码大全》', date: '2006-3', price: 128.00, count: 1 } }, computed: { sum () { return this.books1.price * this.books1.count + this.books2.price * this.books2.count + this.books3.price * this.books3.count + this.books4.price * this.books4.count } }, methods: { down (books1) { this.books1.count = books1.count - 1; } } }) </script> </body> </html>
最新发布
04-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值