剑指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算法精炼的主要内容,如果未能解决你的问题,请参考以下文章

每日算法题 | 剑指offer 二叉树专题 (16) 平衡二叉树

剑指offer-二叉树的镜像

剑指Offer-平衡二叉树

剑指 Offer 55 - II. 平衡二叉树

二叉搜索树的后序遍历序列(剑指offer-23)

剑指 Offer 54. 二叉搜索树的第k大节点