数组中只出现一次的数字,和为S的连续正数序列,和为S的两个数字(剑指offer40-42)c++版本

#include 
#include 
#include 

using namespace std;

class Solution {
     
public:
	//JZ40 数组中只出现一次的数字
	void FindNumsAppearOnce(vector<int> data, int* num1, int *num2);
	int findfirstof1(vector<int> data);
	bool isindex1(int num, int index);
	//JZ41 和为S的连续正数序列
	vector<vector<int> > FindContinuousSequence(int sum);
	//JZ42 和为S的两个数字
	vector<int> FindNumbersWithSum(vector<int> array, int sum);//数组是递增排序的,多对的情况,则输出乘积最小的
};

//JZ40 数组中只出现一次的数字
void Solution::FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
     
	if (data.empty())	return;
	*num1 = 0, *num2 = 0;//任何数^0等于它本身
	int index = findfirstof1(data);
	for (vector<int>::iterator it = data.begin(); it != data.end(); it++) {
     
		//按index位是否为1将数组分成两部分,原问题分解为数组中只出现一次的一个数字
		if (isindex1(*it, index))	*num1 ^= *it;
		else
		{
     
			*num2 ^= *it;
		}
	}
	return;
}
int Solution::findfirstof1(vector<int> data) {
     
	int temp = 0;
	for (vector<int>::iterator it = data.begin(); it != data.end(); it++) {
     
		temp ^= *it;
	}
	int index = 0;
	while (temp) {
     
		if (temp & 1)	return index;
		temp = temp >> 1;
		index++;
	}
	return -1;
}
bool Solution::isindex1(int num, int index) {
     
	if ((num >> index) & 1)	return true;
	return false;
}
//JZ41 和为S的连续正数序列
vector<vector<int> > Solution::FindContinuousSequence(int sum) {
     
	//定义两个指针,第一个指针指向子数组的头,第二个指针指向子数组的尾部
	//当从头到尾的和小于sum,则增加尾;若大于,则增加头。证明后面附出。
	if (sum <= 2)	return vector<vector<int> >();
	int first = 1, last = 2, mid = sum / 2 + 1;
	int sumtemp = 3;
	vector<vector<int> > result;
	while (first < mid) {
     
		if (sumtemp == sum) {
     
			vector<int> temp;
			for (int i = first; i <= last; i++)
				temp.push_back(i);
			result.push_back(temp);
			sumtemp -= first;
			first++;			
		}
		else if (sumtemp < sum) {
     
			last++;
			sumtemp += last;
		}
		else
		{
     
			sumtemp -= first;
			first++;
		}
	}
	return result;
}
//JZ42 和为S的两个数字
vector<int> Solution::FindNumbersWithSum(vector<int> array, int sum) {
     
	int len = array.size();
	if (len <= 1)	return vector<int>();
	int first = 0, last = len - 1;
	int product = INT_MAX;
	int flag = 0;//判断有没有这样的数组存在
	vector<int> result(2);
	while (first < len && first < last) {
     
		int sumtemp = array[first] + array[last];
		if (sumtemp == sum) {
     
			flag = 1;
			int producttemp = array[first] * array[last];
			if (producttemp < product) {
     
				result[0] = array[first];
				result[1] = array[last];
				product = producttemp;
			}
			first++;
		}
		else if (sumtemp < sum) {
     
			first++;
		}
		else{
     
			last--;
		}		
	}
	if (flag == 0)	return vector<int>();
	return result;
}

//JZ40 数组中只出现一次的数字
void test1() {
     
	vector<int> data = {
      1,2,2,3,3,4 };
	int* num1 = new int;
	int* num2 = new int;
	*num1 = 0, *num2 = 0;
	Solution s;
	s.FindNumsAppearOnce(data, num1, num2);
	cout << *num1 << ' ' << *num2;
	delete[] num1;
	delete[] num2;
	return;
}
//JZ41 和为S的连续正数序列
void test2() {
     
	int sum = 100;
	Solution s;
	vector<vector<int> > result = s.FindContinuousSequence(sum);
	int rows = result.size();
	vector<int>::iterator it;
	for (int i = 0; i < rows; i++) {
     
		for (it = result[i].begin(); it != result[i].end(); it++)
			cout << *it << ' ';
		cout << endl;
	}
	return;
}
//JZ42 和为S的两个数字
void test3() {
     
	vector<int> temp = {
      1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 };
	int sum = 21;
	Solution s;
	vector<int> result = s.FindNumbersWithSum(temp, sum);
	for (vector<int>::iterator it = result.begin(); it != result.end(); it++)
		cout << *it << ' ';
	return;
}


int main() {
     
	//test1();
	//test2();
	test3();
	system("pause");
	return 0;
}

附:证明JZ41的正确性。
证明:设连续序列{a0,a1,…,am}是一个不能用上述方法得到的连续序列,{b0,b1,…,bl}是满足bl <= am并且使用上述方法得到的最后一组连续序列。
记{b0,b1,…,bl}的和为sum_b,序列中的第一个元素为first,最后一个元素为last。

  1. b0 < a0。否则,若b0 = a0,由假设知,两个序列相同。若b0>a0,sum_b < sum,与{bi}为满足条件的连续序列矛盾。
  2. 按照上述方法的规则,此时,first++,sum_b -= first,sum_b < sum。下一步,last++。
    case1. sum_b == sum,与假设矛盾
    case2. sum_b < sum, last++.
    case3. sum_b > sum. first++,接下来的情形为case1或者case2.
    由2知,接下来,要么矛盾,要么case1或者case2。如果在case1之前,last或者first等于{ai}的尾部或者头部(注意,两者不可能同时到达,因为case1和case2不可能同时发生)。
    1)若first先到达头部。此时,若last < am,那么sum_b < sum,case2的情形,last会一直增加,直到等于am,矛盾。
    2)若last先到达尾部。此时,若first < a0,说明first先到达头部,与假设矛盾。若first = a0,矛盾。若first sum,first会一直增加,直到等于a0,矛盾。
    得证。

注:JZ42的证明与上述证明相似.

你可能感兴趣的:(剑指offer,算法,算法,c++)