剑指Offer算法精炼
Posted WQP_Ya_Ping
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer算法精炼相关的知识,希望对你有一定的参考价值。
//File:: test.cpp
//Author:: Weiqp
//Date:: 2016-6-4
# if 0
# include <iostream>
using namespace std;
void main()
int n[][3] = 10,20,30,40,50,60;
int (*p)[3];
p = n;
// 10 20 30
cout<<p[0][0]<<", "<<*(p[0]+1)<<", "<<(*p)[2]<<endl;
# endif
//1.二维数组查找(从左下角开始)
# if 0
# include <iostream>
# include <vector>
using namespace std;
bool Find(vector<vector<int> > array,int target)
int m,n,x,y;
m = array.size();//行数
n = array[0].size();//列数
x=m-1;y=0;//坐标定在左下角
while(x>=0 && y<=n-1)
if(target<array[x][y])
x--;//遇小上移
else if(target>array[x][y])
y++;//遇大右移
else
return true;
return false;
int main()
vector<vector<int> > vv(3,vector<int>(4)) ;
int k = 0;
//初始化 vv.size() = 3
for(int i = 0; i<vv.size(); i++)
for(int j = 0; j<4; j++)
vv[i][j] = k;
k++;
cout<<vv.size()<<endl;
//输出
for(int i = 0; i<3; i++)
for(int j = 0; j<4; j++)
cout<<vv[i][j]<<" ";
k++;
cout<<endl;
if(Find(vv,20))
cout<<"OK"<<endl;
else
cout<<"False"<<endl;
# endif
//2.替换字符串空格(前提:原数组后有充足的空间容纳多的字符,所以本次并没有分配新的空间)
# if 0
# include <iostream>
using namespace std;
void replaceSpace(char *str,int length)
if(str == NULL || length <= 0)
return ;
int i = 0;
int OriginalLength = 0;
int SpaceCount = 0;
//计算原数组长度和空格数目
while(str[i] != '\\0')
OriginalLength ++;
if(str[i] == ' ')
SpaceCount ++;
i++;
int NewLength = OriginalLength + 2 * SpaceCount;
if(NewLength < OriginalLength)
return ;
//IndexOriginal指向原数组末尾,IndexDestination指向新数组末尾
int IndexOriginal = OriginalLength;
int IndexDestination = NewLength;
//从后往前赋值
while(IndexOriginal >= 0 && IndexDestination > IndexOriginal )
//替换空格,IndexDestination向前走三步
if(str[IndexOriginal] == ' ')
str[IndexDestination --] = '0';
str[IndexDestination --] = '2';
str[IndexDestination --] = '%';
else
str[IndexDestination --] = str[IndexOriginal];
-- IndexOriginal;
//打印新数组
int j = 0;
while(str[j] != '\\0')
cout<<str[j];
j++;
cout<<endl;
int main()
char str[30] = " abc bh hv ";
int length = sizeof(str)/sizeof(str[0]);
replaceSpace(str, length);
return 0;
# endif
//3.合并两个有序数组
# if 0
# include <iostream>
using namespace std;
void MergeString(char *str1, char *str2)
//判空
if(str1 == NULL || str2 == NULL)
return ;
int i = 0;
int j = 0;
int Len1 = strlen(str1);
int Len2 = strlen(str2);
/*while(str1[i] != '\\0')
Len1 ++;
i++;
while(str1[j] != '\\0')
Len2 ++;
j++;
*/
//定位在str1末尾
int IndexStr1 = Len1 - 1;
//定位在str2末尾
int IndexStr2 = Len2 - 1;
//新数组长度-1,即指向最后一个元素
int NewLen = Len1 +Len2 - 1;
//由后往前将较大的数赋給新数组
while(IndexStr1 >= 0 && IndexStr2 >=0)
if(str1[IndexStr1] > str2[IndexStr2])
str1[NewLen--] = str1[IndexStr1 --];
else
str1[NewLen--] = str2[IndexStr2--];
//将较长数组剩下的元素依次赋給新数组
while(NewLen >= 0 && IndexStr1 >= 0)
str1[NewLen --] = str1[IndexStr1 --];
while(NewLen >=0 && IndexStr2 >= 0)
str1[NewLen --] = str1[IndexStr2 --];
return ;
int main()
char a1[30] = "abcdefgh";//保证有充足的空间容纳a2的元素
char a2[12] = "ijklmonpqrs";
MergeString(a1,a2);
//打印新数组
for(int i = 0; i<20; i++)
cout<<a1[i];
cout<<endl;
return 0;
# endif
//Test
# if 0
# include <iostream>
using namespace std;
int main()
char a[20] = "asbvc";
cout<<sizeof(a)/sizeof(a[0])<<endl;//20
cout<<strlen(a)<<endl; //5
return 0;
# endif
# if 0
//4.反向打印链表元素
# include <iostream>
# include <stack>
using namespace std;
//定义链表节点
typedef struct ListNode
int Value;
ListNode *Next;
;
//方法1::从前往后依次将节点元素压栈然后依次弹出
void PrintListStack(ListNode *Head)
stack<ListNode *> s;
if(Head == NULL)
return ;
ListNode *p = Head;
while(p != NULL)
s.push(p);
p = p->Next;
while(!s.empty())
p = s.top();
printf("%d ",p -> Value);
s.pop();
//方法2::采用递归,每访问一个节点先输出他的后一个节点,在输出他本身
void PrintListRecur(ListNode *Head)
ListNode *p = Head;
if(p != NULL )
if(p ->Next != NULL)
PrintListRecur(p ->Next );
printf("%d ",p ->Value );
# endif
//5.根据前序遍历和中序遍历重建二叉树
/*思想::
前序遍历的第一个元素一定是根节点,又由中序遍历可知位于根节点前的是左子树元素,
位于根节点后的是右子树元素.按此方法递归建立左子树和右子树。
*/
# if 0
# include <iostream>
using namespace std;
//二叉树结点定义
typedef struct BinaryTreeNode
int Value;
BinaryTreeNode *LeftChild;
BinaryTreeNode *RightChild;
;
//构建二叉树
BinaryTreeNode *Construct(int *Preorder, int *Inorder, int Length)
if(Preorder == NULL || Inorder == NULL || Length <=0)
return NULL;
return ConstructCore(Preorder, Preorder +Length -1, Inorder, Inorder +Length -1);
//构造函数
BinaryTreeNode *ConstructCore(int *StartPreorder, int *EndPreorder, int *StartInorder, int *EndInorder)
//第一个元素作为根节点
int rootvalue = StartPreorder[0];
BinaryTreeNode *root = new BinaryTreeNode();
root ->Value = rootvalue;
root ->LeftChild = root ->RightChild = NULL;
if(StartPreorder == EndPreOrder)
if(StartInorder == EndInorder && *StartPreorder == *StartInorder)
return root;
else
throw std::execption("Invalid input.");
//在中序遍历中找根节点
int *rootInorder = StartInorder;
while(rootInorde <= EndInorder && *rootInorder != rootvalue)
rootInorder ++;
while(rootInorde == EndInorder && *rootInorder != rootvalue)
throw std::execption("Invalid input.");
int LeftLength = rootInorder - StartInorder;
int *LeftPreorderEnd = StartPreorder + LeftLength;
//构建左子树
if(LeftLength >0)
root ->LeftChild = ConstructCore(StartPreorder +1, LeftPreorderEnd, StartInorder,rootInorder -1);
//构建右子树
if(LeftLength < EndPreorder - StartPreorder)
root ->RightChild = ConstructCore(LeftPreorderEnd +1, EndPreorder, rootInorder +1,EndInorder);
return root;
# endif
//6.用两个栈模拟一个队列的操作
/*当要向尾部插入一个节点的时候,将节点依次压入stack1;当要删除头节点的时候,如果stack2为空,则将stack1中的元素依次弹出并压入stack2,这时弹出stack2的栈顶即可;如果stack2不为空,则直接弹出
stack2栈顶*/
# if 0
# include <stdio.h>
using namespace std;
//模板类的定义
template <typename T> class CQueue
public:
CQueue(void);
~CQueue();
void appendTail(const T &node);
T deleteHead();
private:
stack<T> stack1;
stack<T> stack2;
;
//向队列插入节点
template <typename T> void CQueue<T>::appendTail(const T &element)
stack1.push(element);
//删除队头节点
template <typename T> void CQueue<T>::deleteHead ()
if(stack2.size() <= 0)
while(stack1.size() > 0)
T &data = stack1.top();
stack1.pop();
stack2.push(data);
if(stack2.size() == 0)
throw new exception("queue is empty");
T head = stack2.top();
stack2.pop();
return head;
# endif
//7.用两个队列模拟一个栈的操作
/*思想:
假设有两个队列Q1和Q2,当二者都为空时,入栈操作可以用入队操作来模拟,可以随便选一个空队列,假设选Q1进行入栈操作,现在假设a,b,c依次入栈了(即依次进入队列Q1),这时如果想模拟出栈操作,则需要将c出栈,因为在栈顶,这时候可以考虑用空队列Q2,将a,b依次从Q1中出队,而后进入队列Q2,将Q1的最后一个元素c出队即可,此时Q1变为了空队列,Q2中有两个元素,队头元素为a,队尾元素为b,接下来如果再执行入栈操作,则需要将元素进入到Q1和Q2中的非空队列,即进入Q2队列,出栈的话,就跟前面的一样,将Q2除最后一个元素外全部出队,并依次进入队列Q1,再将Q2的最后一个元素出队即可。
*/
# if 0
# include <iostream>
using namespace std;
template <typename T> void Stack<T>::Push(T elem) //向队列中添加元素
if (q1.empty() && q2.empty())
q1.push(elem);
else if (!q1.empty())
q1.push(elem);
//q2不为空
else//两个队列不可能同时为空,因为在之前删除元素操作时,有一个必为空队列
q2.push(elem);
template <typename T> void Stack<T>::Pop() //向队列中删除头元素,先进先出
if (q1.empty() && q2.empty())//两个队列都为空,无法删除
cout<<"queue is empty";
if (!q1.empty())
while (q1.size()>1)
q2.push(q1.front());
q1.pop();
q1.pop();
//q2不为空
else //if (!q2.empty() && q1.empty())
while (q2.size()>1)
q1.push(q2.front());
q2.pop();
q2.pop();
# endif
//8.快速排序的实现
# if 0
# include <iostream>
# include <ctime>
using namespace std;
//分割函数
int partition(int *a, int l, int r)
srand(time(0));
int i = int(rand() % (r-l+1) + l);//取l~r随机数
int key = a[i];
a[i] = a[l];
//现在key是基数,a[l]留作安放键值
while(l < r)
while(l < r && a[r] >= key) r--;
a[l] = a[r];//自右向左找到第一个小于基数的,安放在a[l]
while(l < r && a[l] <= key) l++;
a[r] = a[l];//自左向右找到第一个大于基数的,安放在a[r]
a[l] = key;//此时l==r,key左边的值都比它小,右边都比它大
return l;
//递归排序
void Qsort(int *a, int l, int r)
if(l < r)
int m = partition(a, l, r);
Qsort(a, l, m-1);
Qsort(a, m+1, r);
int main()
int a[7];
for(int i = 0; i < 6; i++)
scanf("%d", &a[i]);
Qsort(a, 0, 5);
for(int i = 0; i < 6; i++)
printf("%d ", a[i]);
cout<<endl;
return 0;
# endif
//9.求旋转数组的最小元素,如[1,2,3,4,5]的一个旋转数组[3,4,5,1,2],最小元素是1.
# if 0
#include <stdio.h>
#include <stdlib.h>
int getMaxIdx(int *A, int len)
int low=0, high=len-1, mid;
while(low<high)
mid = low + (high-low + 1)/2;
if(A[mid]>A[low])
low = mid;
else
high = mid-1;
return low;
int getMin(int *A, int len)
if(A==NULL)
return 0;
int maxIdx = getMaxIdx(A, len);
//最小的数在最大的数后面
int minIdx = (maxIdx + 1)%len;
return A[minIdx];
====================测试代码====================
void Test(int* numbers, int length, int expected)
int result = 0;
try
result = getMin(numbers, length);
for(int i = 0; i < length; ++i)
printf("%d ", numbers[i]);
if(result == expected)
printf("\\tpassed\\n");
else
printf("\\tfailed\\n");
catch (...)//捕捉任何异常类型
if(numbers == NULL)
printf("Test passed.\\n");
else
printf("Test failed.\\n");
int main()
// 典型输入,单调升序的数组的一个旋转
int array1[] = 3, 4, 5, 1, 2;
Test(array1, sizeof(array1) / sizeof(int), 1);
printf("%d\\n",getMin(array1, 5));
// 有重复数字,并且重复的数字刚好的最小的数字
int array2[] = 3, 4, 5, 1, 1, 2;
Test(array2, sizeof(array2) / sizeof(int), 1);
printf("%d\\n",getMin(array2, 6));
// 有重复数字,但重复的数字不是第一个数字和最后一个数字
int array3[] = 3, 4, 5, 1, 2, 2;
Test(array3, sizeof(array3) / sizeof(int), 1);
printf("%d\\n",getMin(array3, 6));
// 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
int array4[] = 1, 0, 1, 1, 1;
Test(array4, sizeof(array4) / sizeof(int), 0);
printf("%d\\n",getMin(array4, 5));
// 单调升序数组,旋转0个元素,也就是单调升序数组本身
int array5[] = 1, 2, 3, 4, 5;
Test(array5, sizeof(array5) / sizeof(int), 1);
printf("%d\\n",getMin(array5, 5));
// 数组中只有一个数字
int array6[] = 2;
Test(array6, sizeof(array6) / sizeof(int), 2);
printf("%d\\n",getMin(array6, 1));
// 输入NULL
Test(NULL, 0, 0);
printf("%d\\n",getMin(NULL, 5));
system("pause");
# endif
//10.输入n,求裴波那挈数列的第n项
# if 0
/*方法1::递归(低效率,树形结构分析会发现有大量重复计算)
f(10) = f(9)+f(8) = f(8)+f(7)+f(7)+f(6).....
*/
long long Fibonacci(unsigned int n)
if(n <= 0)
return 0;
if(n == 1)
return 1;
return Fibonacci(n - 1) + Fibonacci(n - 2);
//方法2::由下往上计算,避免重复f(0)+f(1) = f(2)+f(3) = f(4)...
long long Fibonacci(unsigned int n)
int result[2] = 0,1;
if(n < 2)
return result[n];
long long fiboMinusOne = 0;
long long fiboMinusTwo = 1;
long long fiboN = 0;
for(unsigned int i = 2; i <= n; i++)
fiboN = fiboMinusOne + fiboMinusTwo;
fiboMinusOne = fiboMinusTwo;
fiboMinusTwo = fiboN;
return fiboN;
# endif
以上是关于剑指Offer算法精炼的主要内容,如果未能解决你的问题,请参考以下文章