目录
剑指Offer(3)--数组中重复的数字
找出数组中重复的数字,这个数组的特点是:如果长度为n,那么数组中所有的数字都在0~n-1的范围内。
#include<iostream>
using namespace std;
int duplicate(int numbers[], int length)
{
if (numbers == nullptr || length < 1)
return -1;
for (int i = 0; i < length; ++i)
{
while (numbers[i] != i)
{
if (numbers[i] == numbers[numbers[i]])
return numbers[i];
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return -1;
}
int main(void)
{
int arr[6] = { 1,3,4,5,2,5 };
int result = duplicate(arr, 6);
cout << result << endl;
system("pause");
return 0;
}
尽管上述代码中有一个两重循环,但每个数字最多只要交换两次就能找到属于它的位置,因此总的时间复杂度是O(n)。另外所有的步骤都是在输入数组上进行的,不需要分配额外内存,空间复杂度是O(1)。
如果不允许修改原数组,那么可以创建一个辅助数组,然后逐一把原数组的每个数字复制到辅助数组,如果原数组中被复制的数字是m,则把它复制到辅助数组中下标为m的位置。这样下次再放的时候就可以比较出哪个数字是重复的了。
#include<iostream>
using namespace std;
//该方案的时间、空间复杂度均为O(n)
int duplicate(int numbers[], int length)
{
if (numbers == nullptr || length < 1)
return -1;
int *copyNumbers = new int[length];
for (int i = 0; i < length; ++i)
{
copyNumbers[i] = -1;
}
for (int i = 0; i < length; ++i)
{
if (numbers[i] == copyNumbers[i])
return numbers[i];
copyNumbers[numbers[i]] = numbers[i];
}
return -1;
}
int main(void)
{
int arr[6] = { 1,3,4,5,2,5 };
int result = duplicate(arr, 6);
cout << result << endl;
system("pause");
return 0;
}
剑指Offer(4)--二维数组中的查找
在一个二维数组中,每一行从左向右递增,每一列从上到下递增。判断一个整数是否在这个二维数组中。
#include<iostream>
using namespace std;
//rows是行数,cols是列数
bool find(int *matrix, int rows, int cols, int number)
{
if (matrix != nullptr && rows > 0 && cols > 0)
{
int row = 0;
int col = cols - 1;
while (row < rows && col >= 0)
{
if (matrix[row*cols + col] == number)
return true;
else if (matrix[row*cols + col] > number)
--col;
else
++row;
}
}
return false;
}
剑指Offer(16)--数值的整数次方
实现函数double Power(doublebase, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
思路:
当指数为正时,直接计算。当指数为负时,可以先对指数求绝对值,算出次方的结果之后再取倒数。
#include<iostream>
using namespace std;
double Power(double base, int exponent)
{
if (base == 0.0)
{
cout << "invalidInput" << endl;
return -1;
}
int index = 0;
//如果指数为负,那么结果就是 1.0/base的正数次方
if (exponent < 0)
{
index = 1;
exponent = -exponent;
}
double result = 1.0;
for (int i = 1; i <= exponent; ++i)
result *= base;
//说明指数为负
if (index)
result = 1.0 / result;
return result;
}
int main(void)
{
cout << Power(3,-2) << endl;
system("pause");
return 0;
}
思路二:
利用递归进行求解,比如求解的是16,那么求出8次方的结果之后,result*result就是结果。如果求解的是17次方,17/2之后也是8,所以最后还需要判断次方是奇数还是偶数。
#include<iostream>
using namespace std;
double Power(double base, int exponent)
{
if (exponent == 0)
return 1;
if (exponent == 1)
return base;
double result = Power(base, exponent >> 1);
result *= result;
//说明是奇数
if (exponent & 0x1 == 1)
result *= base;
return result;
}
剑指Offer(21)--调整数组顺序使奇数位于偶数前面
两个指针,一个向后,一个向前,当都走到不符合条件时停下,然后交换两元素。接着重复此步骤,直到第一个指针不小于第二个指针。这个思想很重要。
#include<iostream>
using namespace std;
void arrangeArray(int *pData, int length)
{
if (pData == nullptr || length < 1)
return;
int *pBegin = pData;
int *pEnd = pData + length - 1;
while (pBegin < pEnd)
{
//说明是奇数,继续前移找偶数
while ((pBegin < pEnd) &&(*pBegin & 0x1) != 0)
++pBegin;
while ((pBegin < pEnd) && (*pEnd & 0x1) == 0)
--pEnd;
if (pBegin < pEnd)
{
int temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
}
}
}
int main(void)
{
int arr[7] = { 2,4,1,6,3,5,8 };
arrangeArray(arr, 7);
for (int i : arr)
cout << i << " ";
cout << endl;
system("pause");
return 0;
}
剑指Offer(29)--顺时针打印矩阵
题目:
输入一个矩阵,按照从外向里以顺时针打印出每一个数字,例如:输入如下矩阵,则依次打印出的数字为:1、2、3、4、8、12、16、15、14、13、9、5、6、7、11、10。
思路:
固定住左上角和右下角,然后一圈一圈往内输出。
#include<iostream>
using namespace std;
void spiralOrderPrint(int **p, int l, int h)
{
if (l < 0 || h < 0)
return;
int tR = 0;
int tC = 0;
int dR = l - 1;
int dC = h - 1;
while (tR <= dR && tC <= dC)
spiralOrderPrintCore(p, tR++, tC++, dR--, dC--);
return;
}
void spiralOrderPrintCore(int **p, int tR, int tC, int dR, int dC)
{
if (tR == dR)
{
for (int i = tC; i <= dC; ++i)
cout << p[tR][i] << " ";
}
else if (tC == dC)
{
for (int i = tR; i <= dR; ++i)
cout << p[i][tC] << " ";
}
else
{
int curC = tC;
int curR = tR;
while (curC < dC)
{
cout << p[tR][curC] << " ";
++curC;
}
cout << endl;
while (curR < dR)
{
cout << p[curR][dC] << " ";
++curR;
}
cout << endl;
while (curC > tC)
{
cout << p[dR][curC] << " ";
--curC;
}
cout << endl;
while (curR>tR)
{
cout << p[curR][tC] << " ";
--curR;
}
}
}
剑指Offer(39)--数组中出现次数超过一半的数字
#include<iostream>
using namespace std;
/*
思路:数组中有一个数字出现的次数超过数组长度的一半,也就是说它在数组中出现的次数
比剩余的加起来还要多,因此我们可以考虑在遍历数组的时候保存两个值:一个是数组的一个
数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字
相同,则次数加1;如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,
我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字
出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。
*/
bool checkMoreThanHalf(int *numbers, int length, int number);
int MoreThanHalfNum(int *numbers, int length)
{
if (numbers == nullptr || length < 1)
return -1;
int result = numbers[0];
int times = 1;
for (int i = 1; i < length; ++i)
{
if (times == 0)
{
result = numbers[i];
times = 1;
}
else if (numbers[i] == result)
times++;
else
times--;
}
//找到了数组中元素次数最多的,但不一定过半,还需进行验证
if (!checkMoreThanHalf(numbers,length,result))
return -1;
return result;
}
//判断某值在数组中出现的次数是否过半
bool checkMoreThanHalf(int *numbers, int length, int number)
{
int times = 0;
for (int i = 0; i < length; ++i)
{
if (numbers[i] == number)
times++;
}
if (times * 2 <= length)
return false;
return true;
}
P206页的另一种思路也要记住
剑指Offer(40)--最小的k个数
题目:输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
思路一:
基于快速排序中的Partition函数来解决这个问题。如果基于数组的第k个数字来调整,使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的所有数字都位于数组的右边。这样调整之后,位于数组中左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)。时间复杂度是O(n)。
采用这种思路是有限制的。我们需要修改输入的数组,因为函数Partition会调整数组中数字的顺序。
#include<iostream>
#include<algorithm>
using namespace std;
int patition(int *arr, int left, int right)
{
int i = left, j = right - 1;
//int temp = arr[right];
while (left < right)
{
while (arr[i] < arr[right])
++i;
while (arr[j] > arr[right] && j>left)
--j;
if (i < j)
swap(arr[i], arr[j]);
else
break;
}
swap(arr[i], arr[right]);
return i;
}
//input是题目给定的数组,n是input的维度
//output是结果,k是题目要求的维度
void GetLeastNumbers(int *input, int n, int *output, int k)
{
if (input == nullptr || k > n || n < 1 || k < 1)
return;
int start = 0, end = n - 1;
int index = patition(input, start, end);
while (index != k - 1)
{
if (index < k - 1)
{
start = index + 1;
index = patition(input, start, end);
}
else
{
end = index - 1;
index = patition(input, start, end);
}
}
for (int i = 0; i < k; ++i)
output[i] = input[i];
}
int main(void)
{
int arr[] = { 8,4,9,2,6,87,1,67,34,65,99,78 };
int result[10];
GetLeastNumbers(arr, 12, result, 10);
for (int i = 0; i < 10; ++i)
cout << result[i] << " ";
cout << endl;
system("pause");
return 0;
}
思路二:
可以先创建一个大小为k的数据容器来存储最小的k个数字,接下来我们每次从输入的n个整数中读入一个数。
如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中;
如果容器中已有k个数字了,也就是容器已满,此时我们不能再插入新的数字而只能替换已有的数字。
因此当容器满了之后,我们要做3件事情:一是在k个整数中找到最大数;二是有可能在这个容器中删除最大数;三是有可能要插入一个新的数字。如果用一个二叉树来实现这个数据容器,那么我们能在O(logk)时间内实现这三步操作。因此对于n个输入数字而言,总的时间效率就是O(nlogk)。采用了红黑树结构作为容器,当然也可以采用堆来实现。
#include<iostream>
#include<vector>
#include<set>
#include<xfunctional>
using namespace std;
void GetLeastNumbers(const vector<int>& v, multiset<int, greater<int>> &ms,int k)
{
auto iterV = v.begin();
auto iterS = ms.begin();
for (; iterV != v.end(); ++iterV)
{
if (ms.size() < k)
ms.insert(*iterV);
else
{
iterS = ms.begin();
if (*iterV < *iterS)
{
ms.erase(iterS);
ms.insert(*iterV);
}
}
}
}
int main(void)
{
vector<int> v = { 8,4,9,2,6,87,1,67,34,65,99,78 };
multiset<int, greater<int>> ms;
GetLeastNumbers(v, ms,5);
for (auto num : ms)
cout << num << " ";
cout << endl;
system("pause");
return 0;
}
剑指Offer(42)--连续子数组的最大和
整形数组中有整数也有负数,求数组的子数组的最大和。
#include<iostream>
using namespace std;
int greateSum(int *pData, int length)
{
if (pData == nullptr || length < 1)
return -1;
int maxSum = -1000;//当前的连续子数组的最大和
int curSum = 0;
for (int i = 0; i < length; ++i)
{
if (curSum < 0)
curSum = pData[i];
else
curSum += pData[i];
if (curSum > maxSum)
maxSum = curSum;
}
return maxSum;
}
int main(void)
{
int arr[7] = { -3,4,-4,6,3,-1,8 };
int result = greateSum(arr, 7);
cout << result << endl;
system("pause");
return 0;
}
剑指Offer(45)--把数组排成最小的数
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//数组中的最大长度
const int maxNumberLength = 10;
char *strCombine1 = new char[maxNumberLength * 2 + 1];
char *strCombine2 = new char[maxNumberLength * 2 + 1];
int compare(const void *strNumber1, const void *strNumber2);
void minNumber(int *numbers, int length)
{
if (numbers == nullptr || length < 1)
return;
//为二级指针动态初始化
char **strNumbers = (char**)new int[length];
/*
char **strNumbers;
strNumbers=new int[length];
*/
for (int i = 0; i < length; ++i)
{
strNumbers[i] = new char[maxNumberLength + 1];
//函数功能是将数据格式化输出到字符串
sprintf(strNumbers[i], "%d", numbers[i]);
}
/*
_CRTIMP void __cdecl qsort(void*, size_t, size_t,int (*)(const void*, const void*));
compare函数原型:compare( (void *) & elem1, (void *) & elem2 );
*/
qsort(strNumbers, length, sizeof(char*), compare);
for (int i = 0; i < length; ++i)
printf("%s", strNumbers[i]);
cout << endl;
for (int i = 0; i < length; ++i)
delete[] strNumbers[i];
delete[] strNumbers;
}
//compare的使用具有固定格式,百度qsort函数看示例
int compare(const void *strNumber1, const void *strNumber2)
{
strcpy(strCombine1, *(const char**)strNumber1);
strcat(strCombine1, *(const char**)strNumber2);
strcpy(strCombine2, *(const char**)strNumber2);
strcat(strCombine2, *(const char**)strNumber1);
return strcmp(strCombine1, strCombine2);
}
int main(void)
{
int array[4] = { 23,1,45,53 };
minNumber(array, 4);
system("pause");
return 0;
}
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
/*
给定一个字符串类型的数组strs,找到一种拼接方式,使得把所
有字 符串拼起来之后形成的字符串具有最低的字典序。
思路:贪心算法的利用,注意一般证明贪心策略正确是非常困难的,
用对数器进行验证
*/
bool compare(string str1, string str2)
{
return str1 + str2 < str2 + str1 ? true : false;//由小到大排序
}
string lowest(vector<string> strs)
{
if (strs.size() == 0)
return "";
sort(strs.begin(), strs.end(), compare);
string res = "";
for (int i = 0; i < strs.size(); ++i)
res += strs[i];
return res;
}
int main()
{
vector<string> strs;
strs.push_back("b");
strs.push_back("ba");
strs.push_back("kj");
strs.push_back("ca");
cout << lowest(strs) << endl;
system("pause");
return 0;
}
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
bool compare(string str1, string str2)
{
return stoi(str1 + str2) < stoi(str2 + str1) ? true : false;
}
vector<int> lowest(int *numbers,int length)
{
vector<string> strs;
for (int i = 0; i < length; ++i)
strs.push_back(to_string(numbers[i]));
sort(strs.begin(), strs.end(), compare);
vector<int> res;
for (int i = 0; i < strs.size(); ++i)
res.push_back(stoi(strs[i]));
return res;
}
int main()
{
int numbers[] = { 3,32,321 };
vector<int> v = lowest(numbers, 3);
for (auto i : v)
cout << i;
cout << endl;
system("pause");
return 0;
}
剑指Offer(51)--数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字构成一个逆序对。
#include<iostream>
using namespace std;
int InversePairsCore(int *data, int *copy, int start, int end);
int InversePairs(int *data, int length)
{
if (data == nullptr || length < 0)
return 0;
int *copy = new int[length];
for (int i = 0; i < length; ++i)
copy[i] = data[i];
int count = InversePairsCore(data, copy, 0, length - 1);
delete[]copy;
return count;
}
//递归函数的返回值是逆序对的个数
int InversePairsCore(int *data, int *copy, int start, int end)
{
if (start == end)
{
copy[start] = data[start];
return 0;
}
int length = (end - start) >> 1;
int left = InversePairsCore(copy, data, start, start + length);
int right = InversePairsCore(copy, data, start + length + 1, end);
//i初始化为前半段的最后一个数字的下标
int i = start + length;
//j初始化为后半段的最后一个数字的下标
int j = end;
//index是排序数组的下标,从后向前,依此找该位置上的元素
int index = end;
//统计逆序对个数
int count = 0;
while (i >= start && j >= start + length + 1)
{
if (data[i] > data[j])
{
copy[index--] = data[i--];
count += j - start - length;
}
else
copy[index--] = data[j--];
}
for (; i >= start; --i)
copy[index--] = data[i];
for (; j >= start + length + 1; --j)
copy[index--] = data[j];
//左半部分的逆序对+右半部分的逆序对+左右合起来的逆序对
return left + right + count;
}
int main(void)
{
int array[5] = { 23,1,3,53,6 };
int result = InversePairs(array, 5);
cout << result << endl;
system("pause");
return 0;
}
剑指Offer(53)--在排序数组中查找数字
统计一个数字在排序数组中出现的次数。
思路:利用二分查找,找出该数字在数组中第一次出现的位置和最后一次出现的位置,因为是排序数组,即可得出在数组中出现的次数。
#include<iostream>
#include<deque>
#include<vector>
using namespace std;
int getFirstK(int *data, int length, int start, int end, int k)
{
if (start > end)
return -1;
int middle = (start + end) / 2;
if (data[middle] == k)
{
if (middle > start && data[middle - 1] == k)
return getFirstK(data, length, start, middle - 1, k);
else
return middle;
}
else if(data[middle]>k)
return getFirstK(data, length, start, middle - 1, k);
else
return getFirstK(data, length, middle+1, end, k);
}
int getLastK(int *data, int length, int start, int end, int k)
{
if (start > end)
return -1;
int middle = (start + end) / 2;
if (data[middle] == k)
{
if (middle < end && data[middle + 1] == k)
return getFirstK(data, length, middle +1 , end, k);
else
return middle;
}
else if (data[middle]>k)
return getFirstK(data, length, start, middle - 1, k);
else
return getLastK(data, length, middle + 1, end, k);
}
int getNumber(int *data, int length, int k)
{
if (data == nullptr || length < 1)
return -1;
int result = 0;
int first = getFirstK(data, length, 0, length - 1,k);
int last = getLastK(data, length,0, length - 1,k);
if (first < 0 || last < 0)
return -1;
result = last - first + 1;
return result;
}
int main(void)
{
int array[7] = { 1,2,3,3,3,4,5 };
int result = getNumber(array, 7, 3);
cout << result << endl;
system("pause");
return 0;
}
剑指Offer(56)--数组中数字出现的次数
找出一个数组中只出现一次的两个数字,剩下的其他数字都是出现两次。
思路:
异或的性质:任何一个数字异或自己都为0;如果是出现一次的只有一个数字的话,那么对整个数组进行异或,得到的结果就是这个唯一出现一次的数字。现在题中有两个出现一次的数字,所以想办法将它俩分开。
#include<iostream>
using namespace std;
int findFirstBitIs1(int num);
bool IsBit1(int num, int indexBit);
//因为找的是两个数字,而以返回值的形式只能返回一个,
//所以可以使用形参作为传出参数
void searchNum(int *data, int length, int *num1, int *num2)
{
if (data == nullptr || length < 1)
return;
int resultBit = 0;
//数组中的所有元素经过异或运算之后,肯定有某位为1,因为数组
//中有两个只出现一次的数字
for (int i = 0; i < length; ++i)
resultBit ^= data[i];
int indexOf1 = findFirstBitIs1(resultBit);
//将数组中两个唯一的整数分到两个子数组中,这样在每个子数组中
//只有唯一一个出现一次的整数,异或之后,即可得到该整数。
*num1 = *num2 = 0;
for (int i = 0; i < length; ++i)
{
//某整数的第indexOf1位为1
if (IsBit1(data[i], indexOf1))
*num1 ^= data[i];
else
*num2 ^= data[i];
}
}
//在整数num的二进制表示中,找到最右边是1的位
int findFirstBitIs1(int num)
{
int indexBit = 0;
while ((num & 1) == 0 && (indexBit < 8 * sizeof(int)))
{
num = num >> 1;
++indexBit;
}
return indexBit;
}
//判断整数num的二进制表示中从右边起的indexBit是否为1
bool IsBit1(int num, int indexBit)
{
num = num >> indexBit;
return (num & 1);
}
int main(void)
{
int num1 = 0;
int num2 = 0;
int array[8] = { 2,3,4,6,4,3,2,8 };
searchNum(array, 8, &num1, &num2);
cout << num1 << " " << num2 << endl;
system("pause");
return 0;
}
/*
两个整数的位与&,位或|,位异或^运算
先将两个数据转化为二进制数,然后按位进行与运算,同为1结果为1,其它情况结果为0;
先将两个数据转化为二进制数,然后进行按位或运算,只要有一个是1结果为1,不然结果为0;
先将两个数据转化为二进制数,然后进行按位异或运算,只要位不同结果为1,不然结果为0;
*/
在一个数组中除一个数字外,其他数字都出现了3次。找出这个唯一出现一次的数字。
思路:把数组中所有数字的二进制表示的每一位都加起来,如果某一位的和能被3整除,那么那个只出现一次的数字二进制表示中对应的那一位是0;否则就是1。
#include<iostream>
using namespace std;
int findNumber(int *data, int length)
{
if (data == nullptr || length < 1)
return -1;
int num[32] = { 0 };
int bit = 1;
for (int i = 0; i < 32; ++i)
{
for (int j = 0; j < length; ++j)
{
int value = bit&data[j];
if(value!=0)
num[i] += 1;
}
bit = bit << 1;
}
int result = 0;
for (int i = 31; i >= 0; --i)
{
result = result << 1;
result += num[i] % 3;
}
return result;
}
int main(void)
{
int array[10] = { 1,1,3,2,1,11111,3,2,3,2 };
int result = findNumber(array, 10);
cout << result << endl;
system("pause");
return 0;
}
剑指Offer(57)--和为s的数字
题目:
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s,如果有多对数字的和等于s,输出任意一对即可。
思路:
夹逼准则
#include<iostream>
using namespace std;
bool FindNumbersWithSum(int *data, int length, int s,
int *num1, int *num2)
{
if (data == nullptr || length < 2)
return false;
int head = 0;
int tail = length - 1;
while (head < tail)
{
if (data[head] + data[tail] == s)
{
num1 = &data[head];
num2 = &data[tail];
return true;
}
else if (data[head] + data[tail] < s)
++head;
else
--tail;
}
return false;
}
题目二:
和为s的连续正数系列。
输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以结果打印出3个连续序列1-5,4-6和7-8。
思路:
用两个数small和bbig分别表示序列的最小值和最大值。首先把small初始化为1,bbig初始化为2.如果从small到big的序列和大于s,则可以从序列中去掉较小的值,也就是增大small的值。如果从small到big的序列和小于s,则可以增大big,让这个序列包含更多的数字。
#include<iostream>
using namespace std;
/*
small到big区间内的数就是和为sum的数,如果小于sum,则
big++,如果大于small,则small++
*/
void FindContinusSequence(int sum)
{
if (sum < 3)
return;
int small = 1;
int big = 2;
int middle = (1 + sum) / 2;
int curSum = small + big;
//因为序列最少要两个数字,而(1+S)/2+(1+S)/2>S
//所以小一点的数不能超过(1+S)/2
while (small < middle)
{
if (curSum == sum)
Print(small, big);
while (curSum>sum && small<middle)
{
curSum -= small;
small++;
if (curSum == sum)
Print(small, big);
}
big++;
curSum += big;
}
}
void Print(int small, int big)
{
for (int i = small; i <= big; ++i)
cout << i << " ";
cout << endl;
}
剑指Offer(63)--股票的最大利润
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可获得的最大利润是多少?例如,一只股票在某些时间节点的价格为{9,11,8,5,7,12,16,14}。如果我们能在价格为5的时候买入并在价格为16时卖出,则能获得最大的利润为11。
思路:
第一种是找出数组中的所有数对,然后比较差值,时间复杂度是O(n^2)。
第二种是同时记录最小值和最大利润,也就是说,如果在扫描到数组中的第i个数字时,只要我们能够记住之前的i-1个数字中的最小值,就能算出在当前价位卖出时可能得到的最大利润。
#include<iostream>
using namespace std;
int MaxDiff(const int *numbers, int length)
{
if (numbers == nullptr || length < 2)
return 0;
int min = numbers[0];
int maxDiff = numbers[1] - numbers[0];
for (int i = 2; i < length; ++i)
{
if (numbers[i - 1] < min)
min = numbers[i - 1];
int curDiff = numbers[i] - min;
if (curDiff > maxDiff)
maxDiff = curDiff;
}
return maxDiff;
}
剑指Offer(66)--构建乘积数组
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。
思路:把数组B看成由一个矩阵来创建
#include<iostream>
using namespace std;
void multiplyCore(int *numbers1, int *numbers2, int length);
void multiply(int *numbers, int length)
{
int *copy = new int[length];
for (int i = 0; i < length; ++i)
copy[i] = numbers[i];
multiplyCore(numbers, copy, length);
for (int i = 0; i < length; ++i)
cout << copy[i] << " ";
cout << endl;
delete []copy;
}
void multiplyCore(int *numbers1, int *numbers2, int length)
{
if (length > 1)
{
numbers2[0] = 1;
//矩阵中左下半部分每一行的乘积
for (int i = 1; i < length; ++i)
numbers2[i] = numbers2[i - 1] * numbers1[i - 1];
int temp = 1;
for (int i = length - 2; i >= 0; --i)
{
temp *= numbers1[i + 1];
//补上上面每一行中缺失的部分
numbers2[i] *= temp;
}
}
}
int main(void)
{
int array[4] = { 1,2,3,4};
multiply(array, 4);
system("pause");
return 0;
}
实现一个洗牌函数,即随机打乱一组数
#include<iostream>
#include<ctime>
using namespace std;
void GetRandNumber(int array[], int length)
{
if (array == NULL || length == 0)
return;
int value = 0;
int temp = 0;
for (int index = 0; index < length; ++index)
{
value = index + rand() % (length - index);
temp = array[index];
array[index] = array[value];
array[value] = temp;
}
}
int main(void)
{
srand((int)time(0));
int array[] = { 1,2,3,4,5,6,7,8,9,10,11 };
GetRandNumber(array, 11);
for (auto i : array)
cout << i << " ";
cout << endl;
system("pause");
return 0;
}
算法实现求n的阶乘
求大整数n阶乘,在找工作笔试和面试的过程中,不止一次遇到这个问题,用一个for循环迭代出的结果肯定是不行的,即直接用int,默认是32位,它能表示的最大值为2,147,483,647,但是12的阶乘为479,001,600,13的阶乘为6,227,020,800,所以当n为13的时候已经溢出了。所以当n为更大的值时,需要采用巧妙的方法来防止溢出。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#define MAX 40000
int main(void)
{
int n;
while (scanf("%d", &n) != EOF && n >= 1)
{
int i, j;
int arr[MAX];//每一位存储的都是结果
int p, h;//p存储是当前结果的位数,h是每次产生的进位
arr[1] = 1;
p = 1;
//上面对应的是1的阶乘
//接下来求k的阶乘
for (i = 2; i <= n; ++i)
{
//计算阶乘之前先将进位设为0
for (j = 1, h = 0; j <= p; ++j)
{
arr[j] = arr[j] * i + h;
h = arr[j] / 10;
arr[j] = arr[j] % 10;
}
//说明产生了新的位数
while (h > 0)
{
arr[j] = h % 10;
h /= 10;
++j;
}
//p是当前的位数
p = j - 1;
}
for (i = p; i >= 2; --i)
printf("%d", arr[i]);
printf("%d\n", arr[i]);
}
system("pause");
return 0;
}
举个例子,比如说我们现在得到了4!= 24,在数组中是以倒序的方式存储,即42,a[1] = 4,a[2] = 2(数组下标从1开始),现在要计算5!,内层for循环对目前的两位4和2进行处理,即每一位都乘以5,首先a[1] = a[1]*5 + h(进位)= 20,h从0变为2,a[1] = 0;然后a[2] = a[2] * 5 + 2 = 12,h从2变为1,a[2] = 2;这时内层for循环执行结束。
总结1:内层for循环的作用是更新已经存在的位数中的每一位与i的乘积的结果.
接着执行while循环,目前h = 1,a[3] = 1,h 从1变为0,然后退出while循环。处理结束就得到了5! = 120.p的位数增加到3.
总结2:while循环的作用是计算p更新之前的位数之后的进位,比如说p = 2的时候,数组中为42,然后内层for循环把42更新为02,while循环增加进位1,整个数组变为021.
判断IP地址是否合法
#include<iostream>
#include<string>
using namespace std;
bool IsLegal_Ip(const string ipaddr)
{
int len=ipaddr.length();//计算字符串的长度
int stage=0;//该IP地址共有几部分
int stage_value=0;//每个部分的值
int is_stage=0;//判断当前部分是否有值
if(isalpha(ipaddr[0]))//===》if(ipaddr[i]>='a'&&ipaddr[i]<='z'&&ipaddr[i]>='A'&&ipaddr[i]<='Z')
return false;
for(int i=0;i<len;++i)
{
if(isdigit(ipaddr[i]))//==》if(ipaddr[i]>='0' && ipaddr[i]<='9')
{
stage_value=stage_value*10+(ipaddr[i]-'0');
++is_stage;
}
else if(ipaddr[i]=='.')
{
if(stage_value<=255 && is_stage>0)
++stage;
else
return false;
//进行下一轮判断,重新置为0
is_stage=0;
stage_value=0;
}
else//判断有负数的情况
return false;
}
//判断第四部分是否满足IP地址的要求
if(stage_value<=255 && is_stage>0)
++stage;
if(stage==4)
return true;
else
return false;
}
十进制转二进制
#include<iostream>
using namespace std;
void main()
{
int n,i,j=0;
int a[1000];
cin>>n;
i=n;
while(i)
{
a[j]=i%2;
i/=2;
j++;
}
for(i=j-1;i>=0;i--)
cout<<a[i];
cout<<endl;
}
下一个排列
比如求149632的下一个排列为:162349。
#include<iostream>
#include<vector>
using namespace std;
//以149632为例进行分析
void nextPermutation(vector<int>& nums)
{
int k = -1;
for (int i = nums.size() - 2; i >= 0; --i)
{
//找到最大的下标k,使得nums[k]<nums[k+1]
if (nums[i] < nums[i + 1])
{
k = i;
break;
}
}
//这种情况说明vector中的数完全逆序
if (k == -1)
{
reverse(nums.begin(), nums.end());
return;
}
int l = -1;
//从后往前找,找到k之后满足nums[k] < nums[l]的最大的下标i
for (int i = nums.size() - 1; i >= 0; --i)
{
if (nums[i] > nums[k])
{
l = i;
break;
}
}
swap(nums[k], nums[l]);
/*
当初找到的k,说明的是,从k往后的元素是从大到小排列,经过
swap之后依然是从大到小,我们应该弄成从小到大,因为是找第一个
排列大于题给定的序列
*/
reverse(nums.begin() + k + 1, nums.end());
}
int main(void)
{
vector<int> v = { 1,2,3 };
nextPermutation(v);
for (auto i : v)
cout << i << " ";
cout << endl;
system("pause");
return 0;
}
缺失数字
给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。
示例 1:
输入: [3,0,1]
输出: 2
示例 2:
输入: [9,6,4,2,3,5,7,0,1]
输出: 8
方法一:
思路:用等差数列的求和公式求出0到n之间所有的数字之和,然后再遍历数组算出给定数字的累积和,然后做减法,差值就是丢失的那个数字。
class Solution {
public:
int missingNumber(vector<int>& nums) {
int sum = 0, n = nums.size();
for (auto &a : nums) {
sum += a;
}
return 0.5 * n * (n + 1) - sum;
}
};
方法二:
既然0到n之间少了一个数,我们将这个少了一个数的数组合0到n之间完整的数组异或一下,那么相同的数字都变为0了,剩下的就是少了的那个数字了。
class Solution {
public:
int missingNumber(vector<int>& nums) {
int res = 0;
for (int i = 0; i < nums.size(); ++i) {
res ^= (i + 1) ^ nums[i];
}
return res;
}
};