牛客网剑指Offer C++题解

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

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        bool ret = false;
        if (array.size() == 0 || array[0].size() == 0) return ret;
        int row;
        int col;
        int rows = array.size();
        int cols = array[0].size();
        row = 0;
        col = cols-1;
        while(row < rows && col >= 0){
            if (array[row][col] == target){
                ret = true;
                break;
            }else if (array[row][col] > target){
                col--;
            }else{
                row++;
            }
        }
        return ret;
    }
};

坑:需要判断vector为空的情况,不然会段错误......错误......误......

 

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

class Solution {
public:
	void replaceSpace(char *str, int length) {
		if (str == NULL || length == 0) return;
		int blankspacenum = 0;
		int i;
		for (i = 0; i < length; i++) {
			if (str[i] == ' ')
				blankspacenum++;
		}
		int newlen = length + blankspacenum * 2;
		if (newlen == length) return;

		while (newlen >= 0 && newlen > length) {
			if (str[length] == ' ') {
				str[newlen--] = '0';
				str[newlen--] = '2';
				str[newlen--] = '%';
				length--;
			}
			else {
				str[newlen] = str[length];
				newlen--;
				length--;
			}
		}
	}
};

本题最好在str的基础上进行操作,不要new一块新的内存或者将str指向新的一块空间,可能会导致内存泄漏。

 

【从尾到头打印链表】:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int>v;
        if (head == NULL || head->next == NULL) return v; 
        stack<int>s;
        ListNode* p = head;
        while(p){
            s.push(p->val);
            p = p->next;
        }
        while(!s.empty()){
            v.push_back(s.top());
            s.pop();
        }
        return v;
    }
};

 

【用两个栈实现队列】

class Solution
{
public:
    void push(int node) {
        while(!stack2.empty()) stack2.pop();
        while(!stack1.empty()){
            stack2.push(stack1.top());
            stack1.pop();
        }
        stack1.push(node);
        while(!stack2.empty()){
            stack1.push(stack2.top());
            stack2.pop();
        }
    }

    int pop() {
        if(!stack1.empty()){
            int ret = stack1.top();
            stack1.pop();
            return ret;
        }
        return -1;
    }

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

 

【旋转数组的最小数字】:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

class Solution {
public:
	int minNumberInRotateArray(vector<int> rotateArray) {
		if (rotateArray.size() == 0) return 0;
		int left = 0;
		int right = rotateArray.size()-1;
		int mid;
		while (left < right) {
            if(right - left == 1){
                mid = rotateArray[right] > rotateArray[left] ? left:right;
                break;
            }
			mid = (left + right) / 2;
			if (rotateArray[mid] >= rotateArray[left])
				left = mid;
			else if(rotateArray[mid] <= rotateArray[right])
				right = mid;
		}
		return rotateArray[mid];
	}
};

数组可以分成两块,每一块都有序,可以用二分查找的思想。

 

【斐波那契数列】:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

class Solution {
public:
    int Fibonacci(int n) {
        int a[39+3];
        a[0] = 0;
        a[1] = 1;
        a[2] = 1;
        int i;
        for(i=3;i<=n;i++){
            a[i] = a[i-1] + a[i-2];
        }
        return a[n];
    }
};

 

【跳台阶】:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

class Solution {
public:
    int jumpFloor(int number) {
        if(number <= 1) return 1;
        else if (number == 2) return 2;
        int a = 1;
        int b = 2;
        int i;
        for (i=3;i<number;i++){
            int temp = b;
            b += a;
            a = temp;
        }
        return a+b;
    }
};

挺经典的题:假设n级台阶有F(n)种跳法,若第一跳为跳一个台阶,则剩下的n-1个台阶有F(n-1)种跳法,若第一跳是两个台阶,则剩下的台阶有F(n-2)种跳法,同时等式F(n) = F(n-1) + F(n-2)成立(n>2)

 

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

class Solution {
public:
    int jumpFloorII(int number) {
        return pow(2,number-1);
    }
};

同上:F(n) = F(n-1) + F(n-2) + ... + F(1) + 1;

上式恰好是2^(n-1);

 

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

class Solution {
public:
    int rectCover(int number) {
        if (number <= 2) return number;
        int a = 1;
        int b = 2;
        int i;
        for (i=3;i<number;i++){
            int temp = b;
            b += a;
            a = temp;
        }
        return a+b;
    }
};

思路与上面【跳台阶】的一样:F(n) = F(n-1) + F(n-2)(n>2),F(n) = n(0<=n<=2)

 

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

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

n = n & (n-1)每次将最低位的1变为0

 

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

class Solution {
public:
    double Power(double base, int exponent) {
        if (exponent == 0) return 1.0;
        if (fabs(base - 0.0) < 0.0000001) return 0.0;
        int flag = 1;
        if (exponent < 0){
            flag = 0;
            exponent = (-exponent);
        }
        double res = 1.0;
        while(exponent--){
            res *= base;
        }
        if (flag)
            return res;
        else
            return 1.0/res;
    }
};

 

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

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        int num1 = 0;
        for (auto x : array)
            if (x%2)
                num1++;
        
        int p1 = 0;
        int p2 = num1;
        int *a = new int [array.size()];
        for (auto x : array){
            if (x%2)
                a[p1++] = x;
            else
                a[p2++] = x;
        }
        
        int i;
        for (i=0;i<array.size();i++)
            array[i] = a[i];
        
        delete []a;
    }
};

 

【链表中倒数第k个结点】:输入一个链表,输出该链表中倒数第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) {
        int flag = 1;
        int i;
        ListNode* pRes = pListHead;
        for (i=0;i<k;i++){
            if (pListHead)
                pListHead = pListHead->next;
            else {
                flag = 0;
                break;
            }
        }
        if (flag == 0) return nullptr;
        
        while(pListHead){
            pListHead = pListHead->next;
            pRes = pRes->next;
        }
        return pRes;
    }
};

用两个指针,一个指针先走k-1步(注意链表长度可能小于k),接着另一个指针从头开始,第一个指针结束的时候,第二个指针就是倒数第k个。

 

【反转链表】:输入一个链表,反转链表后,输出新链表的表头。

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

 

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

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == nullptr)
            return pHead2;
        if(pHead2 == nullptr)
            return pHead1;
        ListNode* pCur = nullptr;
        if(pHead1->val < pHead2->val){
            pCur = pHead1;
            pHead1 = pHead1->next;
        }else{
            pCur = pHead2;
            pHead2 = pHead2->next;
        }
        pCur->next = nullptr;
        ListNode* resHead = pCur;
        while(pHead1 && pHead2){
            if (pHead1->val < pHead2->val){
                pCur->next = pHead1;
                pCur = pHead1;
                pHead1 = pHead1->next;
                pCur->next = nullptr;
            }else{
                pCur->next = pHead2;
                pCur = pHead2;
                pHead2 = pHead2->next;
                pCur->next = nullptr;
            }
        }
        
        if (pHead1)
            pCur->next = pHead1;
        else if (pHead2)
            pCur->next = pHead2;
        return resHead;
    }
};

 

【顺时针打印矩阵】:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

class Solution {
public:
	vector<int> printMatrix(vector<vector<int> > matrix) {
		vector<int>v;
		if (matrix.size() == 0 || matrix[0].size() == 0)
			return v;

		if (matrix.size() == 1) {
			return matrix[0];
		}

		if (matrix[0].size() == 1) {
			for (auto x : matrix) {
				v.push_back(x[0]);
			}
			return v;
		}
		int rows = matrix.size();
		int cols = matrix[0].size();

		int row = 0;
		int col = 0;

		while (rows > 1 && cols > 1) {
			int trow = row;
			int tcol = col;
			char flag = 'r';
			while (1) {
				v.push_back(matrix[row][col]);
				switch (flag) {
				case 'r':
					col++;
					if (col == tcol + cols - 1)
						flag = 'd';
					break;
				case 'l':
					col--;
					if (col == tcol)
						flag = 'u';
					break;
				case 'u':
					row--;
					if (row == trow)
						flag = 'r';
					break;
				case 'd':
					row++;
					if (row == trow + rows - 1)
						flag = 'l';
					break;
				}
				if (row == trow && col == tcol)
					break;
			}
			row += 1;
			col += 1;
			rows -= 2;
			cols -= 2;
		}
		if (rows == 1) {
			int i;
			for (i = col; i < col + cols; i++)
				v.push_back(matrix[row][i]);
		}
		else if (cols == 1) {
			int i;
			for (i = row; i < row + rows; i++)
				v.push_back(matrix[i][col]);
		}
		return v;
	}
};

思路比较直白,每次打印一个圈的外层,当打印长度小于2的时候结束循环,处理长度为1的情况。

 

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

class Solution {
public:
    void push(int value) {
        s.push(value);
        if (minStack.empty())
            minStack.push(value);
        else{
            int min = minStack.top();
            min = min < value ? min:value;
            minStack.push(min);
        }
    }
    void pop() {
        s.pop();
        minStack.pop();
    }
    int top() {
        return s.top();
    }
    int min() {
        return minStack.top();
    }
    
private:
    stack<int> s;
    stack<int> minStack;
};

用两个栈,其中一个保存用户数据,一个保存着当前用户数据的最小值的辅助栈。例如用户压入:5,6,1,12。那么辅助栈为5,5,1,1。

 

【栈的压入、弹出序列】:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if (pushV.size() == 0 || popV.size() == 0)
            return true;
        int pushPos = 0;
        int popPos = 0;
        stack<int> s;
        while(popPos < popV.size()){
            int key = popV[popPos++];
            if (!s.empty() && s.top() == key){
                s.pop();
                continue;
            }
            while(pushPos < pushV.size() && pushV[pushPos] != key){
                s.push(pushV[pushPos++]);
            }
            if (pushPos < pushV.size() && pushV[pushPos] == key){
                pushPos++;
                continue;
            }
            return false;
        }
        return true;
    }
};

用两个栈模拟压栈出栈过程。从出栈序列开始,如果栈顶不等于出栈序列,则继续压栈,若入栈序列已经找不到相应的入栈的整数时,则说明不可能存在这样的出栈序列。若栈顶等于出栈序列,则退栈。

 

【从上往下打印二叉树】:从上往下打印出二叉树的每个节点,同层节点从左至右打印。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int>v;
        if (root == nullptr)
            return v;
        queue<TreeNode*>s;
        s.push(root);
        while(!s.empty()){
            TreeNode* cur = s.front();
            s.pop();
            v.push_back(cur->val);
            if(cur->left)
                s.push(cur->left);
            if(cur->right)
                s.push(cur->right);
        }
        return v;
    }
};

树的广度优先遍历,用队列。。。

 

【二叉搜索树的后序遍历序列】:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

class Solution {
public:
	bool VerifySquenceOfBST(vector<int> sequence) {
        if (sequence.size() == 0) 
            return false;
		return VerifySquenceOfBST(sequence, 0, sequence.size() - 1);
	}

	bool VerifySquenceOfBST(vector<int> sequence, int l, int r) {
        if (l >= r)
            return true;
		int key = sequence[r];
		int i;
		for (i = l; i < r; i++) {
			if (sequence[i] > key)
				break;
		}

		int j;
		for (j = i; j < r; j++) {
			if (sequence[j] < key)
				return false;
		}
		bool left = true;
		if (i > l) {
			left = VerifySquenceOfBST(sequence, l, i - 1);
		}

		bool right = true;
		if (i < r) {
			right = VerifySquenceOfBST(sequence, i, r-1);
		}
		return (left && right);
	}
};

步骤:

找到左子树和右子树,检查右子树有没问题(左子树肯定没问题),有问题返回false,否则继续递归,直到只剩下一个节点。

 

【二叉树中和为某一值的路径】:输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        vector<int>tv;
        Dfs_FindPath(root,expectNumber,tv);
        return v;
    }
    
    void Dfs_FindPath(TreeNode* root,int expectNumber,
                                      vector<int>tv){
        if (root == nullptr)
            return ;
        
        tv.push_back(root->val);
        if (root->left)
            Dfs_FindPath(root->left,expectNumber,tv);
        if (root->right)
            Dfs_FindPath(root->right,expectNumber,tv);
        
        if(root->left == nullptr && root->right == nullptr){
            int sum = 0;
            for(auto x : tv)
                sum += x;
            if (sum == expectNumber)
                v.push_back(tv);
        }
        
    }

private:
    vector<vector<int> >v;
};

深度优先遍历。。。

 

【复杂链表的复制】:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if (pHead == nullptr)
            return nullptr;
        
        RandomListNode* tHead = pHead;
        while(tHead){
            RandomListNode* newNode = new RandomListNode(tHead->label);
            newNode->next = tHead->next;
            tHead->next = newNode;
            tHead = newNode->next;
        }
        
        tHead = pHead;
        while(tHead){
            RandomListNode* temp = tHead->next;
            if(tHead->random)
                temp->random = tHead->random->next;
            else
                temp->random = nullptr;
            tHead = temp->next;
        }
        
        tHead = pHead;
        RandomListNode* pRes = tHead->next;
        RandomListNode* tpRes = pRes;
        while(tHead){
            tHead->next = pRes->next;
            if (tHead->next == nullptr){
                break;
            }
            pRes->next = tHead->next->next;
            tHead = tHead->next;
            pRes = pRes->next;
        }
        return tpRes;
    }
};

先在原链表的基础上复制出一份相同的链表(先忽略自由指针)例如A->A*->B->B*->C->C*,接着再处理自由指针。那么奇数位的节点为原节点,偶数位的节点是复制链表节点。

 

【字符串的排列】:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

class Solution {
public:
	vector<string> Permutation(string str) {
		vector<string> v;
		if (str.size() == 0)
			return v;
		Permutation(str,0,v);
        sort(v.begin(),v.end());
		return v;
	}

	void Permutation(string str,int begin,vector<string>&v) {
		if (begin == str.length()-1) {
			v.push_back(str);
			return;
		}	
		int i;
		for (i = begin; i < str.length(); i++) {
			if (i != begin && str[i] == str[begin])    //重复不交换
				continue;
			swap(str[i], str[begin]);
			Permutation(str,begin+1,v);
			swap(str[i], str[begin]);        //恢复原样
		}

	}
};

 

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

class Solution {
public:
	int MoreThanHalfNum_Solution(vector<int> numbers) {
		if (numbers.size() <= 0)
			return 0;
		int len = numbers.size();
		map<int, int>m;
		for (auto x : numbers) {
			auto iter = m.find(x);
			int l = 1;
			if (iter == m.end())
				m.insert(pair<int,int>(x, 1));
			else {
				iter->second = iter->second + 1;
				l = iter->second;
			}
			if (len / 2 < l)
				return x;
			
		}
		return 0;
	}
};

 

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

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int sum = array[0];
        int i;
        int max = -1000000000;
        for (i=1;i<array.size();i++){
            if (sum < 0)
                sum = array[i];
            else
                sum += array[i];
            if (sum > max)
                max = sum;
        }
        return max;
    }
};

主要前面累加的值为大于等于0时,对当前的数都是有正作用的。但一旦前面累加的为负,则应该抛弃。

 

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

class Solution {
public:
	int NumberOf1Between1AndN_Solution(int n)
	{
		int e = 10;
		int sum = 0;
		while (1) {
			int x = n / e;
			int y = n / (e / 10) % 10;
			if (y > 1)
				sum += x * (e / 10) + (e / 10);
			else if (y == 1)
				sum += x * (e / 10) + n % (e/10) + 1;
			else
				sum += x * (e / 10);
			e *= 10;
			if (x == 0) break;
		}
		return sum;
	}
};

从个位开始算起一直到最高位。对个位来说,数字从0-9不断循环,例如213,个位 一共循环了20次,所以个位为1的数字一个有21+1=22个。对于十位为1是10-19,110-119,210-213,一共2*10+4=24个。对于百位为1是100-199,一共100个,所以一共:22+24+100=146个1。

 

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

bool cmp(const int &a, const int &b) {
	stringstream ss1;
	ss1 << a << b;
	stringstream ss2;
	ss2 << b << a;
	return ss1.str() < ss2.str();
}
class Solution {
public:
	string PrintMinNumber(vector<int> numbers) {
		std::sort(numbers.begin(),numbers.end(),cmp);
		stringstream ss;
		for (auto x : numbers) {
			ss << x;
		}
		return ss.str();
	}
};

自定义排序方式,两个数m和n,如果mn>nm,那么排序方式是:nm,mn。

 

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

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if (index <= 0)
            return 0;
        
        int* a = new int [index];
        a[0] = 1;
        int newindex = 1;
        int* p2 = a;
        int* p3 = a;
        int* p5 = a;
        while(newindex < index){
            a[newindex] = Mins(*p2*2,*p3*3,*p5*5);
            while(*p2*2 <= a[newindex])
                p2++;
            
            while(*p3*3 <= a[newindex])
                p3++;
            
            while(*p5*5 <= a[newindex])
                p5++;
            
            newindex++;
        }
        int res = a[index-1];
        delete []a;
        return res;
    }
    
    int Mins(int a,int b,int c){
        int min = a;
        min = min > b ? b : min;
        min = min > c ? c : min;
        return min;
    }
};

 

后一个丑数是前面丑数的某一个*2或*3或*5的结果。。。

 

【两个链表的第一个公共结点】:输入两个链表,找出它们的第一个公共结点。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
	ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
		if (pHead1 == nullptr || pHead2 == nullptr)
			return nullptr;

		set<void*>s;
		while (pHead1) {
			auto iter = s.find(pHead1);
			if (iter == s.end())
				s.insert(pHead1);

			pHead1 = pHead1->next;
		}

		while (pHead2) {
			auto iter = s.find(pHead2);
			if (iter != s.end())
				break;
			pHead2 = pHead2->next;
		}

		return pHead2;
	}
};

或:

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
	ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
		if (pHead1 == nullptr || pHead2 == nullptr)
			return nullptr;

        ListNode* tpHead1 = pHead1;
        ListNode* tpHead2 = pHead2;
        int len1 = 0;
		while (tpHead1) {
			tpHead1 = tpHead1->next;
            len1++;
		}
        
        int len2 = 0;
		while (tpHead2) {
			tpHead2 = tpHead2->next;
            len2++;
		}
        
        //长的链表先走k步
        if (len1 > len2){
            int k = len1-len2;
            while(k--){
                pHead1 = pHead1->next;
            }
            
        }else{
            int k = len2-len1;
            while(k--){
                pHead2 = pHead2->next;
            }
        }
        
        while(pHead1 && pHead2 && pHead1 != pHead2){
                pHead1 = pHead1->next;
                pHead2 = pHead2->next;
        }
        return pHead1;
	}
};

 

【数组中只出现一次的数字】:一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。

class Solution {
public:
	void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
		*num1 = 0;
		*num2 = 0;
		int num = 0;
		for (auto x : data)
			num ^= x;

		int n = 0;
		while (1) {
			if (num & 1)
				break;
			n++;
			num = num >> 1;
		}

		for (auto x : data) {
			if ((x >> n) & 1)
				*num1 ^= x;
			else
				*num2 ^= x;
		}
	}
};

异或操作特点:b^a^b=a,所以第一个将所有数异或一遍,结果就是不同的两个数异或的结果,两数不同,则至少有一位是1,找到最低位为1的位数n,遍历整个vector,将n位为1和不为1的分开,最终的结果就是这两个只出现一次的数。

 

【和为S的连续正数序列】:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int> >v;
        if (sum <= 2)
            return v;
        
        int s = 1;
        int b = 2;
        int sq = 3;
        while(b <= (sum+1)/2){
            if (sq == sum){
                vector<int>tv;
                int i;
                for (i=s;i<=b;i++)
                    tv.push_back(i);
                v.push_back(tv);
                b++;
                sq += b;
            }
            else if (sq < sum){
                b++;
                sq += b;
            }else{
                sq -= s;
                s++;
            }
        }
        return v;
    }
};

 

【左旋转字符串】:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

class Solution {
public:
    string LeftRotateString(string str, int n) {
        if (n < 0 || n > str.length())
            return "";
        if (n == 0)
            return str;
        string lstr = str.substr(0,n);
        string rstr = str.substr(n,str.length()-n);
        reverse(lstr.begin(),lstr.end());
        reverse(rstr.begin(),rstr.end());
        string s = lstr + rstr;
        reverse(s.begin(),s.end());
        return s;
    }
};

 

【翻转单词顺序列】:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

class Solution {
public:
	string ReverseSentence(string str) {
		reverse(str.begin(), str.end());
		int len = str.length();
		char* cstr = new char[len + 1];
		strcpy(cstr, str.c_str());
		int i;
		char* pBegin;
		char* pEnd;
		pBegin = cstr;
		pEnd = cstr;
		for (; *pEnd != '\0'; pEnd++) {
			if (*pEnd == ' ' && pEnd > pBegin) {
				reverse(pBegin, pEnd);
				pBegin = pEnd + 1;
				continue;
			}
		}
		if (pEnd-pBegin > 1)
			reverse(pBegin, pEnd);
		str = cstr;
		delete[]cstr;
		return str;
	}
};

 

【孩子们的游戏(圆圈中最后剩下的数)】:每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

class Solution {
public:
	int LastRemaining_Solution(int n, int m)
	{
		if (n < 1 || m < 1)
			return -1;

		int last = 0;
		int i;
		for (i = 2; i <= n; i++) {
			last = (last + m + i) % i;
		}
		return last;
	}
};

约瑟夫环问题,假设n,m中去掉的第一个数为k,那么k = (m-1) % n。接下来要屏蔽去掉的数字对后续操作的影响,所以我们可以对剩下的数字进行重新编号,例如k+1编号为0,k+2编号为1...k-1编号为n-2。再重复上述操作,那么最后剩下的数就是最后一轮编号为0的数。所以倒推,从最后一轮为0一直倒推到刚开始的时候。从n->n-1的公式为x' = (x-k-1+n) % n,其中x为n轮的下标,x'为n-1轮的下标。那么倒推就是求x的值。x = (x+k+1+n) % n,其中k = (m-1) % n,所以x = (x+(m-1)%n+1+n) % n = (x+m+n) %n。

 

【把字符串转换成整数】:将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

class Solution {
public:
    int StrToInt(string str) {
       if (str.length() < 1)
           return 0;
        
        long long num = 0;
        bool minus = false;
        if (str[0] == '+'){
            str = str.substr(1,str.length());
        }else if (str[0] == '-'){
            minus = true;
            str = str.substr(1,str.length());
        }
        
        num = StrToLong(str,minus);
        
        
        return (int)num;
    }
    
    long long StrToLong(string str,bool minus){
        long long num = 0;
        int i;
        int flag = (minus == true)? -1:1;
        for (i=0;i<str.length();i++){
            if(str[i]>='0' && str[i]<='9'){
                num = num * 10 + flag * (str[i] - '0');
                if ((!minus && num > 0x7FFFFFFF) || 
                    (minus && num < (signed int)0x80000000))
                    return 0;
            }
            else{
                return 0;
            }
        }
        return num;
    }
};

注意判断时候溢出。。。

 

 

未完持续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值