剑指Offer_Code

数组查找

(1)二维数组中的查找

题目描述

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

 

解题思路

考虑到二维数组是从左到右递增,从上到下递增.因此对每一行i,从右上角j=array[i].size-1(下标值是数组行大小-1)开始比较,若数组当前值array[i][j]比target大,那么target值只能是在后面的行(i++);若array[i][j]<target,那么target可能在当前行,往前面列查找(j--);相等返回.

想法和LeetCode_TwoSum是不是很像呢!

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        int i=0,r = array.size(),j=array[0].size()-1;
        while(i<r&&j>=0)
        {
            if(array[i][j]>target) j--;
            else if(array[i][j]==target) return 1;
            else i++;
        }
        return 0;
    }
};
# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        i=0
        r = len(array)
        j = len(array[0]) - 1
        while(i<r and j>=0):
            if(target<array[i][j]): 
                j=j-1
            elif(target==array[i][j]):
                return 1
            else: i=i+1
        return 0

(c++: && Python:and;Matlab:&)扎心了

(2)替换空格(指针的使用)

题目描述

请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

  •  

char* str="hello world ";这个指针指向常量字符串,存储在静态存储区,是只读的,不能被修改。

而char str[]="hello world"是一个局部变量数组,存储在栈上的内存空间,可以被修改。

class Solution {
public:
	void replaceSpace(char *str,int length) {
        char *p,*end;
        p=str;
        int Len=0,newLen = 0,space=0;
        while(*p!='\0')
        {
            if (*p==' ')
                space++;
            Len ++;
            p++;
        }
        newLen = Len + space*2;
        end = str + newLen ;
        *end = '\0';
        end--;
        p = str + Len - 1;
        while(p>=str)
        {
            if(*p!=' ')
                *end-- = *p;
            else
            {
                *end--='0';
                *end--='2';
                *end--='%';
            }
            p--;
        }
	}
};

解题思路:

char *str ,字符串数组,将空格替换成'%20',替换增加了两个字符,需要将空格后的字符往后挪到.先统计所有空格的个数,计算字符串的新长度.设了新的指针end指向新长度的末尾.p指向原字符串末尾,从最后开始挪动到end指向的位置.

(3)链表的使用

题目描述

输入一个链表,从尾到头打印链表每个节点的值。

解题思路:

   a.遍历链表,值存到堆栈.最后将堆栈存的值top给vector  

   b.递归,总感觉把递归想得很复杂,写不出来,看了别人答案写出来的....

class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        stack <int> stk;
        vector<int> a;
        while(head!=NULL)
        {
            stk.push(head->val);//入栈
            head = head->next;
        }
        while(!stk.empty())
        {
            a.push_back(stk.top());//取值
            stk.pop();//出栈
        }
        return a;
    }
};
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> a;
        if(head!=NULL){
            if(head->next!=NULL){
                a=printListFromTailToHead(head->next);
            }
            a.push_back(head->val); 
            
        }
        return a;
    }
};

(4)重构二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

 

解题思路

前序第一个元素就是该树的根节点,创建根节点;

中序根节点左边的节点就是该树的左子树的所有节点(n个)的中序列表(前序列表就是根节点后n个),递归构建该左子树;

同理右子树可建.

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if(pre.empty()||vin.empty())
            return NULL;
        if(pre.size()!=vin.size())
            return NULL;
        int vlen=vin.size();
        int i,ind,leftLen=0,rightLen=0;
        for(i=0;i<vlen;i++)
            if(vin[i]==pre[0]){
                ind = i;
                break;
            }
            else leftLen++;
        rightLen = vlen -leftLen -1;
        vector<int> left_pre,left_vin,right_pre,right_vin;
        for(i=0;i<ind;i++){//分别提取左右子树的前序,中序
            left_vin.push_back(vin[i]);
            left_pre.push_back(pre[i+1]);
        }
        for(i=ind+1;i<vlen;i++){
            right_vin.push_back(vin[i]);
            right_pre.push_back(pre[i]);
        }
        
        TreeNode* T=new TreeNode(pre[0]);//创建节点
        if(leftLen>0)
            T->left= reConstructBinaryTree(left_pre,left_vin);
        else T->left = NULL;
        if(rightLen>0)
            T->right=reConstructBinaryTree(right_pre,right_vin);
        else
            T->right = NULL;
         return T;
    }
};

(5)栈&队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

解题思路

用两个模拟队列,s1主要用来入队列,s2用来出队列;

当s2为空时,从s1出栈(后进s1的先出到s2的栈底)到s2的元素满足先进先出.

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }
    
    int pop() {
        int t=-1;
        if(!stack2.empty())
        {
            t = stack2.top();
            stack2.pop();
        }
        else if(!stack1.empty())
        {
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }
            t = stack2.top();
            stack2.pop();
        }
     return t;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

(6)查找和排序

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解题思路

二分查找.但是因为该数组是非递减数组的一个旋转,也就是在两个递增序列的临界处不满足递增性质.这边代码的处理,感觉和生硬..mid直接是后面的那个元素的下标.

class Solution {
public:
    int minNumberInRotateArray(vector<int> array) {
        int len = array.size();
        int start=0,mid=0,end=len-1;
        if(len==0) 
            return 0;
        while(start<=end)
        {
            mid = (start+end)/2;
            if(start==end-1){
                mid=start+1;
                break;
            }
            if(array[start]<=array[mid])
            {
                start = mid;
            }
            else
                end = mid;
        }
        return array[mid];
    }
};

(7)斐波那契数列(递归和循环)

题目描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39

class Solution {
public:
    int Fibonacci(int n) {
        if(n==0)
            return 0;
        if(n==1)
            return 1;
        int i,N,N1,N2;
        N1 = 0;
        N2 = 1;
        for(i=2;i<=n;i++)
        {
            N = N1+N2;
            N1 = N2;
            N2 = N;
        }
        return N;
        //RE:return Fibonacci(n-1)+Fibonacci(n-2); // F(n)=F(n-1)+F(n-2)
    }
};

(8)跳台阶(递归和循环)

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路

类似(7)的做法,递归!

跳n阶的时候,选择跳1阶或者2阶,那么相应就是(n-1)和(n-2)阶的时候:f(n)=f(n-1)+f(n-2)

bug:有先后顺序,比如1,2和2,1是不一样的跳法

class Solution {
public:
    int jumpFloor(int n) {
        int x=0,y=(n+1)/2,sum=0;
        for(x=0;x<=n;x++)
        {
            if(n==x+2*y)
                sum++;
            else if(n<x+2*y)
                y--;
        }
        return sum;
    }
};
class Solution {
public:
    int jumpFloor(int n) {
       if(n==1)
           return 1;
        if(n==2)
            return 2;
        int i,N,N1=1,N2=2;
        for(i=3;i<=n;i++)
        {
            N=N1+N2;
            N1=N2;
            N2=N;
        }
        return N;
    }
};

(9)变态跳台阶(递归和循环)

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路:

f(n) = f(n-1) +...+f(1)+f(0) ; f(1)=1,f(0)=1,展开计算会发现,其实f(n)=2^(n-1)

class Solution {
public:
    int jumpFloorII(int n) {
        int i=0,N=0,N1=1;
        if(n==1)
            return 1;
        /*for(i=1;i<=n;i++)//f(n)=f(n-1)+...f(1)+f(0)
        {
            N=N+N1;
            N1=N;
        }
        return N;//3ms
        */
        return pow(2,n-1);//3ms,424k
    }
};

(10) 矩形的覆盖(递归和循环)

题目描述

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

解题思路

敲重点!(记住这是考递归和循环)小矩形无非是横着放(就相当于还要覆盖2*(n-1)的矩形)或竖着放(必须放两个小矩形,就相当于还要覆盖2*(n-2)的矩形)

class Solution {
public:
    int rectCover(int n) {
        int i=0,N1=1,N2=2,N;
        if(n<=2)
            return n;
        for(i=3;i<=n;i++)
        {
            N = N1+N2;
            N1 = N2;
            N2 = N;
        }
        return N;
    }
};

(11)二进制数中1的个数

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

解题思路

(参考:http://blog.csdn.net/gogogo_sky/article/details/71550423)

主要考察的是位位运算,五种位运算有:

(1)&(与)–有0则0;无0则1; 

(2)|(或)–有1则1,无1则0; 

(3)^(亦或)–相同为0,不同为1; 

(4)>>右移(最右边的位被抛弃) 

正数,最左边添0;00001010>>3=00000001 

负数,最左边添1;10001010>>3=11110001 

(5)<<左移(最左边的位被抛弃)–最右边统一添0; 

(正数)00001010<<3=01010000 

 

(负数)10001010<<3=01010000 

 

2.求一个 数二进制中1的个数: 

class Solution {
public:
     int  NumberOf1(int n) {
        int N=n,sum=0;
        while(N)
        {
            sum++;
            N=N&(N-1);
        }
         return sum;
     }
};

(12)数值的整数次方

题目描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

解题思路:

要做的很直接,考察代码的完备性.考虑exp以及base的特殊值.

对于求n次方,也可以先求n/2在平方

class Solution {
public:  //3ms,392k
        double Power(double base, int exponent) {
         if(base==0)
            return 0.0;
        if(exponent==0)
            return 1.0;
        else if(exponent==1)
            return base;
         // a^n = a^(n/2)*a(n/2) (odd) *a
         int exp=abs(exponent);
         double p = Power(base,exp>>1);//a^(n/2)
         p *= p;
         if(exp&0x1)//odd exp
             p=p*base;
         if(exponent<0)
             p=1.0/p;
         return p;
        }
    /*//4ms,492k
    double Power(double base, int exponent) {
        int flag = exponent%2,exp=abs(exponent);//1 odd,0 even
        double p = 1;
        if(base==0)
            return 0.0;
        if(exponent==0)
            return 1.0;
        else if(exponent==1)
            return base;
        while(exp>0)
        {
            p = base * p;
            exp -- ;
        }
        if(exponent<0)
            p = 1.0 / p;
        return p;
    }*/
};

(13)调整数组中奇数和偶数的顺序

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路:

考虑当前位置i的元素为奇数还是偶数,为奇数则继续遍历下个元素(更新当前数组中第一个奇数的位置j);为偶数,寻找之后数组中的第一个奇数位置j,并把i到j之间的偶数往后挪动,替换i位置的偶数为寻找到的奇数(array[j]被覆盖之间先保存下来).j主要作用就是指向要开始寻找奇数的位置,不用重复遍历数组.

class Solution {
public: //2ms,480k
    void reOrderArray(vector<int> &array) {
        int i,j=1,k,tmp,len = array.size();
        for(i=0;i<(len-1)&&j<len;i++)
        {
            if(array[i]%2)//odd
            {
                j=i+1;
                continue;
            }
            while((array[j]%2==0)&&j<len)//find first odd
                j++;
            if(j==len)
                break;
            if(array[j]%2)//move even number backward,replace even num in array[i] with odd number array[j]
            {
                tmp = array[j];
                for(k=j;k>i;k--)
                {
                    array[k]=array[k-1];
                }
                array[i] = tmp;
            }
        }
            
    }
};

(14) 链表的倒数第K个

 

 

题目描述

输入一个链表,输出该链表中倒数第k个结点。

解题思路

首先判断链表为空和倒数K个节点不存在的情况.

设置两个指针遍历链表,第一个指针先走k-1个节点,第二个指针开始走;

当第一个指针指向最后一个节点的时候,第二个节点指向倒数第K个节点.

/*struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        //stack<int> stack1;
        int len=0,i,ck=0;
        ListNode* cur;
        ListNode* pTail = pListHead;
        if(pListHead==NULL||k==0)
            return NULL;
        /*
        while(pTail!=NULL) // 额外遍历一次链表得到长度,浪费时间
        {
            len++;
            pTail = pTail->next;
        }
        if(len<k)
            return NULL;// to short
        pTail = pListHead;
        while(pListHead!=NULL&&ck<k)
        {
            ck ++;
            pListHead = pListHead->next;
        }
        */
        for(ck=0;ck<k;ck++)//在头指针往前走k-1次的时候就判断,链表长度是否大于k
        {
            if(pListHead!=NULL)
            {
                pListHead = pListHead->next;
            }
            else 
                return NULL;
        }
        while(pListHead)
        {
            pTail = pTail->next;
            pListHead = pListHead->next;
        }
        return pTail;
    }
};

(15)反转链表

 

 

题目描述

输入一个链表,反转链表后,输出链表的所有元素。

解题思路

首先排除链表为空或者只有一个节点的情况.

指针遍历链表的时候,反转每个节点的next指向.设置pre,cur,nex分别指向遍历时候的前一个指针,当前指针,以及下一个指针.主要是在反转的时候,注意不要断链,也就是丢失nex.

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode* pre=pHead;
        ListNode *nex=pHead,*cur=pHead;
        if(pHead==NULL|| pHead->next==NULL)
            return pHead;
        while(cur)
        {
            nex = cur->next;
            cur->next = pre;
            pre  = cur;
            cur = nex;
        }
        pHead->next = NULL;
        return pre;
    }
};

(16)合并有序链表

 

 

题目描述

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

解题思路

首先考虑存在某个链表为空的情况;

当链表都不空的情况下,假设最小元素在链表1的第一个节点,此时相当于将链表2的元素逐个按序插入链表1;pre,cur1指向遍历链表1时候的前一个元素和当前元素;cur2,nex指向遍历链表2的当前元素和下一个元素.cur1指向元素小于cur2指向元素,则链表1的指向元素分别往后遍历;cur1指向元素大于cur2指向元素,则首先记录链表2的下一个遍历元素nex,修改cur2元素指向cur1,pre的下个元素指向cur元素,即完成cur2元素插入到链表1.

最后需判断是链表1先遍历完还是链表2,将pre的下个元素指向其余元素,完成全部元素的合并.

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        ListNode *cur1=pHead1,*cur2=pHead2,*nex=pHead2,*pre=pHead1;
        if(pHead1==NULL || pHead2==NULL)
            return pHead1 != NULL ? pHead1:pHead2;
        if(cur2->val<cur1->val)
        {
            int tmp = cur1->val;
            cur1->val = cur2->val;
            cur2->val = tmp;
        }
        while(cur1 && cur2)
        {
            if(cur1->val<=cur2->val)
            {
                pre = cur1;
                cur1 = cur1->next;
            }
            else
            {
                nex = cur2->next;
                cur2->next = cur1;
                pre->next = cur2;
                cur2=nex;
            }
        }
        pre->next = cur1==NULL?cur2:cur1;
        return pHead1;
    }
};

(17)树的子结构

 

 

题目描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

解题思路

首先考虑树为空的情况;

当两棵树都非空的情况下,判断以当前根节点pRoot1为子树的结构与pRoot2为子树的结构是否相同?不同再递归判断pRoot1左右子树是否存在与pRoot2树结构相同?

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1&&pRoot2)//判断以当前根节点pRoot1为子树的结构与pRoot2为子树的结构是否相同?不同再递归判断pRoot1左右子树是否存在与pRoot2树结构相同?
            return SameTree(pRoot1,pRoot2)||HasSubtree(pRoot1->left,pRoot2)||HasSubtree(pRoot1->right,pRoot2);
        else 
            return false;
    }
    bool SameTree(TreeNode* pRoot1,TreeNode* pRoot2)
    {
        if(pRoot2==NULL)
            return true;
        if(pRoot1==NULL||pRoot1->val!=pRoot2->val)
            return false;
        return SameTree(pRoot1->left,pRoot2->left)&&SameTree(pRoot1->right,pRoot2->right);
    }
};

(18) 二叉树的镜像

 

 

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。

解题思路

(1)递归实现:镜像二叉树,就是交换左右子树.先交换当前节点的左右子树指针,再递归镜像操作左右子树.

(2)非递归实现:借助栈,保存当前子树的根节点,每次取出一个节点(一棵子树)交换左右子树.

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        TreeNode *tmp = pRoot->left;
        if(pRoot)
        {
            pRoot->left = pRoot->right ;
            pRoot->right = tmp;
            Mirror(pRoot->left);//递归交换左右子树
            Mirror(pRoot->right);
        }
    }
};
/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot==NULL)
            return;
        stack<TreeNode*> s;//非递归,借助栈保存树节点
        s.push(pRoot);
        while(!s.empty())
        {
            TreeNode *cur = s.top();
            s.pop();
            TreeNode *tmp = cur->left;
            cur->left = cur->right;
            cur->right = tmp;
            if(cur->left)
                s.push(cur->left);
            if(cur->right)
                s.push(cur->right);
        }
    }
};

(19)顺时针打印矩阵

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        int sc=0,sr=0,ec=matrix[0].size()-1,er=matrix.size()-1;
        vector<int> a;
        while(sc<=ec&&sr<=er)
        {
            int curc=sc,curr=sr;
            if(sr==er)
            {
                while(curc<=ec){ //只有一行
                    a.push_back(matrix[sr][curc++]);
                }
            }
            else if(sc==ec)
            {
                while(curr<=er)//只有一列
                    a.push_back(matrix[curr++][sc]);
            }
            else
            {
                while(curc<ec)//从左到右
                    a.push_back(matrix[sr][curc++]);
                while(curr<er)//从上到下
                    a.push_back(matrix[curr++][ec]);
                while(curc>sc)//从右到左
                    a.push_back(matrix[er][curc--]);
                while(curr>sr)//从下到上
                    a.push_back(matrix[curr--][sc]);
            }
            sc++;
            sr++;
            ec--;
            er--;
        }
        return a;
    }
};

(20)包含 min 函数的栈

题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

stack<int> s;
stack<int> min_val;  //记录当前深度的栈, 对应的最小值
    void push(int value) {
    	s.push(value);
    	//调整 最小值的 栈
    	if(min_val.empty()||min_val.top()>value)
			min_val.push(value);
		else if(min_val.top()<value)
        {
        	int tmp=min_val.top();
        	min_val.push(tmp);
		}
    }
    void pop() {
        if(!s.empty())
		{
			s.pop();
			min_val.pop();
		}
    }
    int top() {
	 	return s.top();
	}
    int min() {
       	return min_val.top();
	}

(21)栈的压入、弹出序列

题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

 bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size()!=popV.size())
        	return false;
        if(pushV.size()==0)
        	return true;
        stack<int> s;
        int i=0,j=0;
		while(pushV[i]!=popV[j])
			s.push(pushV[i++]);   //元素入栈,直到第一个出栈元素
		while(!s.empty())
		{
			if(s.top()==popV[j])
			{
				//cout<<s.top()<<endl;
				s.pop();
				j++;
			}
			else if(i<pushV.size())
			{
				s.push(pushV[i++]);
			}
			else
				break;	//退出条件,输入序列已经在栈内,并且栈顶元素不是输出序列元素 
		}
		if(s.empty())
			return true;
		else 
			return false;
		
    }
    

(28) 数组中出现次数超过一半的数字

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

  int MoreThanHalfNum_Solution(vector<int> numbers) {
		if(numbers.empty())
			return 0;
    	int i,val=numbers[0],num=0,count=0;
		for(i=0;i<numbers.size();i++)
		{
			if(num==0)
				val = numbers[i];   //不相同数字相互抵消
			if(numbers[i]==val)
				num++;
			else
				num--;
		}
		//cout<<val<<endl; 
		for(i=0;i<numbers.size();i++) //最后留下的数字,判断出现次数 是否 超过一半
		{
			if(val==numbers[i])
				count++;
		}
		if(count<=numbers.size()/2)
			return 0;
		else
			return val;
    }

(29)最小的K个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

思路:

降序排序,使用冒泡,只需K次即可

vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
    	int i=0,j=0,key=0,n=input.size();
    	vector<int> arr;
		if(k>n||input.empty()) 
			return arr;
		for(i=0;i<k;i++)
		{
			for(j=i+1;j<n;j++)
			{
				if(input[j]>input[j-1])   
                //冒泡排序,相邻两个数进行比较,每次最小值冒泡到最底部位置
				{
					key = input[j-1];
					input[j-1]=input[j];
					input[j]=key;
				}
			}
			arr.push_back(input[n-i-1]);
		}
		return arr;
    }

(30)连续子数组的最大和

题目描述

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

思路:

1)两重 for 循环,O(N*N)
2)线性时间算法
    该算法在每次元素累加和小于0时,从下一个元素重新开始累加

 int FindGreatestSumOfSubArray(vector<int> array) {  //两重循环 O(n*n)
    	int i,j,max,n=array.size(),sum;
    	if(array.empty())
    		return -1;
    	max = array[0];
    	for(i=0;i<n;i++)
		{
			sum = array[i];
			for(j=i+1;j<n;j++)
			{
				sum+=array[j];
				if(sum<0)
				{
					//cout<<array[i]<<','<<array[j]<<endl;
					sum-=array[j];
					break;
				}
				if(max<sum)
				{
					max = sum;
				}
			}
			//cout<<array[i]<<"->"<<array[j]<<endl;
			if(max<sum)  //数组全部为负数的情况?
			{
				max = sum;
			}	
		}
		return max;
    }
    
   int FindGreatestSumOfSubArray_On(vector<int> array) {	//O(n)
    	int i,j,max,n=array.size(),sum=0;
    	max = array[0];
    	for(i=0;i<n;i++)
		{
			sum +=array[i];
			if(max<sum)
				max = sum;
			if(sum<0)			//当前累加和小于0,则从下一个元素开始考虑连续子数组的和 
				sum=0;
		}
		return max;
    }

(31) 整数中1出现的次数(从1到n)

题目描述

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。\

思路:

1) 穷举,对区间内的每个数都判断,包含1的个数(复杂度为O(logn))  -- 整体复杂度为 O(nlogn) 
2)O(logn) https://blog.csdn.net/yi_afly/article/details/52012593
 

int hasOne(int n)
    {
    	int i,sum=0;
    	int k,v;
    	while(n)
    	{
    		v = n%10;
			if(v==1)
				sum++;
			n = n/10;
		}
    	return sum;
	}
    int NumberOf1Between1AndN_Solution(int n)
    {
    	int i,sum=0;
    	for(i=1;i<=n;i++)
    	{
			int num =hasOne(i);
			sum += num;
    	//	cout<<num<<endl;
		}
		return sum;
    }
int NumberOf1Between1AndN_Solution(int n)
    {
    	int round=n,weight,base=1,count=0;
		while(round)
    	{
			weight = round%10;		//参考博客介绍,数学规律,对数字的各个位上 ,分析1出现的次数 
			round /=10;
			count +=round*base;
			if(weight==1)
				count+=(n%base)+1;
			else if(weight>1)
				count+=base;
			base *= 10;    		
		}
    	return count;
	}

(32)把数组排成最小的数

题目描述

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

思路:

1)排序算法,主要是重新定义这里的cmp。数字/字符 ab>ba,则认为b<a

// AC code,别人过的代码,调用了sort和to_string,我本地不能通过
 string PrintMinNumber(vector<int> numbers) {
        int len = numbers.size();
        if(len == 0) return "";
        sort(numbers.begin(), numbers.end(), cmp);
        string res;
        for(int i = 0; i < len; i++){
            res += to_string(numbers[i]);
        }
        return res;
    }
    static bool cmp(int a, int b){
        string A = to_string(a) + to_string(b);
        string B = to_string(b) + to_string(a);
        return A < B;
    }

(33) 丑数 

题目描述

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

思路:

1) 暴力求解,从1开始,对每个数判断是不是丑数(while(n) n/=2; while(n) n/=3; while(n) n/=5; ),累计到N个丑数
2)提前计算,
    丑数:2^i * 3^j *5^k
    利用额外的数组,判断当前可能的丑数中(之前的丑数 *2或*3或*5得到的数),最小的加入到数组内 

//第二种解法
int min(int a,int b,int c)
	{
		int m = (a<b)?a:b;
		return m<c?m:c;
	}
    int GetUglyNumber_Solution(int index) {
    	int i,j,num=1;
		vector<int> ugly;
		ugly.push_back(1);
		int p2,p3,p5;
		p2=p3=p5=0;
		while(ugly.size()<index)
		{
			int min_u = min(ugly[p2] *2,ugly[p3]*3,ugly[p5]*5);
			ugly.push_back(min_u);
			if(ugly[p2]*2<=min_u)
				p2++;
			if(ugly[p3]*3<=min_u)
				p3++;
			if(ugly[p5]*5<=min_u)
				p5++;
		}
		return ugly[index-1];
    }

(34)第一个只出现一次的字符

题目描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)

思路:

1) 所有字母包括 a-z,A-Z 
 设置 flag表示该字母出现的次数,index记录该字母出现的首次位置
 遍历字符串,查找,flag为1,index最小的返回 

 int FirstNotRepeatingChar(string str) {
    	int i,min=str.size();
	 	if(str.size()==0)
		 	return -1;
		int flag[52]={0},index[52]={0},ind;
	    for(i=0;i<str.size();i++)
	    {
			char cur = str[i];
			if(cur>='a'&&cur<='z')	
				ind = cur-'a';
			else if(cur>='A'&&cur<='Z')	
				ind = cur-'A'+26;
			if(flag[ind]==0)
				index[ind] = i;
			flag[ind]++;
		}
		for(i=0;i<52;i++)
		{
			cout<<i<<','<<flag[i]<<','<<index[i]<<endl;
			if(flag[i]==1)
			{
				if(index[i]<min)
					min = index[i];
			}
		}
		if(min==str.size())
			return -1;
		else
			return min;
    }

(35)数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

输入: 
1,2,3,4,5,6,7,0
输出: 
7

思路:

1)可以借助归并排序,计算逆序对的个数,这边是从尾部开始合并,方便计算逆序对个数

	long long InversePairsCore(vector<int> &data,vector<int> &reg,int start,int end)
 	{
 		int k=end,mid = (start+end)/2;
 		if(start==end)
 			return 0;
		long long cnt=0;
 		cnt+=InversePairsCore(data,reg,start,mid);
 		cnt+=InversePairsCore(data,reg,mid+1,end);
 		//cnt+=Merge(data,reg,start,end);

 		int start1=start,end1=mid,start2=mid+1,end2=end;
		while(start1<=end1&&start2<=end2)
		{
			if(data[end1]>data[end2])
			{
				cnt+=end2-mid;	//!! 从后往前 计算 逆序的个数,因为 左右数组分别有序, 左边最大的数 大于 右边最大的数, 逆序的pair个数为 end2-(mid+1) -1 = end2-mid 
				reg[k--]=data[end1--];
			}
			else
				reg[k--]=data[end2--];
		} 
		while(start1<=end1)
			reg[k--]=data[end1--];
		while(start2<=end2)
			reg[k--]= data[end2--]; 
		for(k=start;k<=end;k++)
			data[k]=reg[k];
		return cnt;
	 }
    int InversePairs(vector<int> data) {//形参data,不会 将改变的值 传递回main函数 
        int cnt=0,i;
        int start = 0,end = data.size()-1;
        if(start<end)
        {
        	vector<int> reg;
        	for(i=0;i<data.size();i++)
				reg.push_back(data[i]);
        	cnt	=InversePairsCore(data,reg,0,data.size()-1)%1000000007; 
		}
		return cnt;
    }

 

Offer》 1. 赋值运算函数 2. 单例设计模式 3. 二维数组中查找目标值 4. 替换字符串中的空格 5. 从尾到头打印链表 6. 由前序和中序遍历重建二叉树 7. 用两个栈实现队列 8. 求旋转数组的最小数字 9. 斐波那契数列的第n项(青蛙跳台阶) 10. 二进制中1的个数 11. 数值的整数次方 12. 打印1到最大的n位数 13. O(1)时间删除链表节点 14. 使数组中的奇数位于偶数前面 15. 找链表中倒数第K个节点 16. 输出反转后的链表 17. 合并两个有序链表 18. 判断二叉树A中是否包含子树B 19. 二叉树的镜像 20. 顺时针打印矩阵 21. 包含min函数的栈 22. 判断一个栈是否是另一个栈的弹出序列 23. 层序遍历二叉树 24. 后序遍历二叉搜索树 25. 二叉树中和为某值的路径 26. 复杂链表的复制 27. 二叉搜索树转换为双向链表 28. 打印字符串中所有字符的排列 29. 数组中出现次数超过一半的数字 30. 找出最小的K个数 31. 连续子数组的最大和 32. 从1到整数n中1出现的次数 33. 把数组中的数排成一个最小的数 34. 求第N个丑数 35. 第一个出现一次的字符 36. 数组中逆序对的个数 37. 两个链表的第一个公共节点 38. 数字在排序数组中出现的次数 39. 二叉树的深度 40. 数组中只出现一次的两个数,而其他数都出现两次 41. 和为s的连续整数序列 42. 翻转字符串 43. n个骰子的点数及出现的概率 44. 扑克牌的顺子 45. 圆圈中最后剩下的数 46. 1+2+3+...+n的和 47. 不用加减乘除做加法 48. 不能被继承的类 49. 字符串转换为整数 50. 树中两个节点的最低公共祖先 51. 找出重复的数 52. 构建乘积数组 53. 正则表达式匹配 54. 表示数值的字符串 55. 字符流中第一个不重复的字符 56. 链表中环的入口节点 57. 删除链表中重复的节点 58. 二叉树的下一个节点 59. 对称的二叉树 60. 按之字形顺序打印二叉树 61. 把二叉树打印成多行 62. 序列化二叉树 63. 二叉搜索树的第K个节点 64. 数据流中的中位数 65. 滑动窗口的最大值 66. 矩阵中的路径 67. 机器人的运动范围
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值