题目1:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。如把字符串 abcdef 左旋转 2 位得到字符串 cdefab。请实现字符串左旋转的函数,要求对长度为 n 的字符串操作的时间复杂度为 O (n),空间复杂度为 O (1)。
对 m 和 n 求最大公约数,然后将数组分为 gcd (m,n) 组分别进行循环移位。
void Rotate2(string &str, int nRCnt) { int nLen = str.length(); int nNumOfGroup = Gcd(nLen, nRCnt); int nElemInSub = nLen / nNumOfGroup; // 整个数组分为nNumOfGroup组,每组有nElemInSub个元素 int i = 0, j = 0; for(i = 0; i < nNumOfGroup; i++) { char cTemp = str[i]; // 每组内循环nElemInSub-1次 for (j = 0; j < nElemInSub - 1; j++) str[(i + j * nRCnt) % nLen] = str[(i + (j + 1) * nRCnt) % nLen]; str[(i + j * nRCnt) % nLen] = cTemp; } }int _tmain(int argc, _TCHAR* argv[]){ //char aszStr[] = "Modified by yuucyf"; //LeftRotate(aszStr, 8); //printf("%s/n",aszStr); string strStr = "abcdefg"; Rotate2(strStr, 3); printf("%s/n",strStr.c_str()); return 0;}
题目2:给定一个字符串,找出不含有重复字符的最长子串的长度
例如:
给定 "abcabcbb" ,没有重复字符的最长子串是 "abc" ,那么长度就是3。
给定 "bbbbb" ,最长的子串就是 "b" ,长度是1。
给定 "pwwkew" ,最长子串是 "wke" ,长度是3。请注意答案必须是一个子串,"pwke" 是 子序列 而不是子串。
思路:
滑块思想,最大窗口。快慢指针 i,j 结合 hash。
(1)快指针 j 所在元素不重复,更新 max,将快指针 j 元素在 hash 表中的标记为出现,后移 j
(2)快指针 j 所在元素重复,慢指针后移,此时将慢指针 i 元素在 hash 表中的标记清除。此时并不关心是谁重复,重复元素前的元素都要清除掉。
不断重复上面 2 步,知道 i 或 j 到达字符串末尾。时间复杂度 o (n)
class Solution {
public int lengthOfLongestSubstring(String s) {
int []hash = new int [500];
int max = 0;
int i = 0, j = 0;
while (i < s.length() && j
if(hash[s.charAt(j)] == 0) {
hash[s.charAt(j)] = 1;
j++;
max = (j - i) > max ? (j - i) : max;
} else {
hash[s.charAt(i)] = 0;
i++;
}
}
return max;
}
}
题目3:
输入 n 个整数,找出其中最小的 K 个数。
例如输入 4,5,1,6,2,7,3,8 这 8 个数字,则最小的 4 个数字是 1,2,3,4 。
利用堆排序,特别适用于海量数据中寻找最大或者最小的 k 个数字。即构建一个大堆容器,初始化大小为 k,变量初始数,如初始数组大小小于等于 k 直接返回,如果大于 k,则选择数组的前 k 个元素,填充堆,然后调整为最大堆。调整完之后,继续从初始数组中拿出一个元素,如果该元素比大堆的堆顶小,则替换堆顶,继续调整为最大堆,如果大于等于堆顶则直接丢弃,不作调整。
注意:大堆还是小堆的选择很重要,不是寻找最小的 k 个元素就要选择小堆,而且恰恰相反。寻找最小的 k 个数,其实就是寻找第 k 个大的元素,即寻找 k 个数中最大的,不断调整堆,堆得元素个数是 k,堆顶是最大值,遍历完初始数组后,堆中存在的元素即使我们所要寻找的 k 个最小元素。
//堆排序:构建堆,不断调整的过程,从最后一个不是叶子节点的节点开始。 static public ArrayListGetLeastNumbers_Solution1(int[] input, int k) { ArrayList res = new ArrayList(); if (input==null||input.length==0||input.length return res; } int []maxHeap = new int[k]; //初始化堆 for (int i = 0; i < maxHeap.length; i++) { maxHeap[i] = input[i]; } //将初始化的堆调整为最大堆 for (int i = (maxHeap.length-1)/2; i >=0 ; i--) { adjustHeap(maxHeap, i); } //遍历初始数组不断调整最大堆 for (int i = k; i if (maxHeap[0]>input[i]) { maxHeap[0] = input[i]; adjustHeap(maxHeap, 0); } } for (int i = 0; i < maxHeap.length; i++) { res.add(maxHeap[i]); } return res; } static void adjustHeap(int maxHeap[],int i){ int index = i; int lchild=2*i+1; //i的左孩子节点序号 int rchild=2*i+2; //i的右孩子节点序号 if(index<=(maxHeap.length-1)/2) { //寻找子节点中最大的节点 if (lchild index = lchild; } if (rchild index = rchild; } if (i!=index) { //将节点与最大的子节点交换 int tmp = maxHeap[index]; maxHeap[index] = maxHeap[i]; maxHeap[i] = tmp; //交换后,子树可能不满足最大推,递归调整。 adjustHeap(maxHeap, index); } }
题目4:一个台阶总共有 n 级,如果一次可以跳 1 级,也可以跳 2 级,求总共有多少总跳法,并分析算法的时间复杂度。
首先我们考虑最简单的情况:如果只有 1 级台阶,那显然只有一种跳法,如果有 2 级台阶,那就有两种跳的方法了:一种是分两次跳,每次跳 1 级;另外一种就是一次跳 2 级。
现在我们再来讨论一般情况:我们把 n 级台阶时的跳法看成是 n 的函数,记为 f (n)。当 n>2 时,第一次跳的时候就有两种不同的选择:一是第一次只跳 1 级,此时跳法数目等于后面剩下的 n-1 级台阶的跳法数目,即为 f (n-1);另外一种选择是第一次跳 2 级,此时跳法数目等于后面剩下的 n-2 级台阶的跳法数目,即为 f (n-2)。
因此 n 级台阶时的不同跳法的总数 f (n) = f (n-1) + f (n-2)。
我们把上面的分析用一个公式总结如下:
/ 1 (n=1)
f(n) = 2 (n=2)
\ f(n-1) + (f-2) (n>2)
分析到这里,相信很多人都能看出这就是我们熟悉的 Fibonacci 序列。(O (n))
#include "stdafx.h"
#include
using namespace std;
int JumpStep(int n){
if (n <= 0) return 0;
if (n == 1 || n == 2) return n;
return (JumpStep(n-1) + JumpStep(n-2));
}
int _tmain(int argc, _TCHAR* argv[])
{
int nStep = 0;
cout << "请输入台阶数:";
cin >> nStep;
cout << "台阶数为" << nStep << ",那么总共有" << JumpStep(nStep) << "种跳法." << endl;
return 0;
}
题目5:
n 个数字(0,1,…,n-1)形成一个圆圈,从数字 0 开始,每次从这个圆圈中删除第 m 个数字(第一个为当前数字本身,第二个为当前数字的下一个数字), 当一个数字删除后,从被删除数字的下一个继续删除第 m 个数字。
求出在这个圆圈中剩下的最后一个数字。
在一个数组中不断
的进行遍历,从 Index 0 开始进行数,当 Index 值等于 m 的时候,就把该位置值设置为退出标志 (0), 不断的进行重复,但是要小心一个点就是当遇到 Index 值等于 n 的时候,重新设置 Index 值从 0 开始。
#include "stdafx.h"
#include
int RemainLastOne(int i32Count, int i32Pos){
assert(i32Count > 0 && i32Pos > 0);
int *pi32ArrNum = new int[i32Count];
assert(NULL != pi32ArrNum);
for (int i32I = 0; i32I < i32Count; i32I++)
pi32ArrNum[i32I] = i32I+1;
int i32ExitPos = 0;
int i32CurPos = 0;
int i32ExitCnt= 0;
while(i32ExitCnt != i32Count-1)
{
if (pi32ArrNum[i32CurPos] != 0)
i32ExitPos++;
if (i32ExitPos == i32Pos)
{
pi32ArrNum[i32CurPos] = 0; //设置退出标志位(对应的数组元素置为0)。
i32ExitPos = 0;
i32ExitCnt++;
}
i32CurPos++;
if (i32CurPos == i32Count)
i32CurPos = 0;
}
int RetVal = 0;
for (int i32J = 0; i32J < i32Count; i32J++)
{
if (pi32ArrNum[i32J] != 0)
{
RetVal = pi32ArrNum[i32J] - 1;
}
}
delete [] pi32ArrNum;
return RetVal;
}
int RemainLastOne_2(int i32Count, int i32Pos){
assert(i32Count > 0 && i32Pos > 0);
// if there are only one integer in the circle initially,
// of course the last remaining one is 0
int i32Value = 0;
// find the last remaining one in the circle with n integers
for (int i32I = 1; i32I <= i32Count; i32I++)
i32Value = (i32Value + i32Pos) % i32I;
return i32Value;
}
int _tmain(int argc, _TCHAR* argv[])
{
_tprintf(_T("The last one is %d.\n"), RemainLastOne(20, 4));
return 0;
}
题目6:
给定一个 32 位有符号整数,将整数中的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
注意:
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0。
利用最后一位是翻转后的第一位。取到最后一位后,然后利用这一位的十进制表示法可以得到这一位在最高位时表示的整数。然后每一位的表示的整数相加就是最后翻转的整数。在本算法中需要注意的有两点:1 负的整数数字位数少一位 2 负整数对 10 进行求返回的是负数。Math.pow (x,y) 表示 x 的 y 次幂
class Solution {
public int reverse(int x) {
long a = 0;
int b =0;
if(x>=0){
b=(x+"").length();//正数
}
else{
b=(x+"").length()-1;//负数有一个负号
}
while (x!=0){
for(int i=0;i
int a1 = x%10;//当前最后一位数字 ,负数则显示的是负数-25%10 等于 -5
x=(x-a1)/10;//把最后一位数字剔除掉的新数字
a += (int) (a1*Math.pow(10,b-i-1)); //相当于将每次的最后一位乘以当前位在十进制中的权重。234 = 2*10^2+3*10^1+4*10^0
}
}
if((a>Math.pow(2,31)-1)||(a-1)*Math.pow(2,31))){ //超过范围的返回0
return 0;
}
return (int) a;
}
}
题目7:
请实现一个函数,用来判断一颗二叉树是不是对称的。
注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
首先分析下这个对称二叉树,也就是一个二叉树中间对称。所以我们可以使用递归的思想,首先以根节点以及其左右子树,左子树的左子树和右子树的右子树相同,左子树的右子树和右子树的左子树相同。两个条件都要符合,所以我们第一个传根节点的左子树和右子树,先判断左右子树根结点的比较。然后分辨对左子树的左子树和右子树的右子树。左子树的右子树和右子树的左子树进行判断。只有两个条件都满足则返回的是 true,一层一层递归进入,则可以得到结果
/*public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; }}*/public class Solution { boolean isSymmetrical(TreeNode pRoot) { /* 思路:首先根结点以及其左右子树,左子树的左子树和右子树的右子树相同, 左子树的右子树,和右子树的左子树相同。我们采用递归的方式 */ if(pRoot == null){ return false; } return comRoot(pRoot.left, pRoot.right); } public static boolean comRoot(TreeNode left, TreeNode right){ if(left == null){ return right == null; } if(right == null){ return false;//能执行到这一步,说明他的左子树肯定是不为空,此时比较右子树如果为空,那么肯定返回false } if(left.val != right.val){ return false; } //能执行到这一步,说明其传进来的左子树和右子树不为null,且对应值相等,此时我们只需要,进行递归比较 //传进来的左子树的左子树和传进来右子树的右子树。传进来的左子树的右子树和传进来的右子树的左子树。 return comRoot(left.left, right.right) && comRoot(left.right, right.left); }}
题目8:请实现两个函数,分别用来序列化和反序列化二叉树
其实这个里面包含一个二叉树的序列化而反序列化,这里的序列化指代的是将一颗二叉树没有访问的顺序,然后通过序列化,将这颗二叉树转化为有访问的顺序。所以序列化的时候给我的是一个根节点,然后我通过先序遍历,将这颗树进行序列化,然后转化为一个字符串,空的节点指针则为#表示。
而反序列化,则是给我一个序列化的字符串,然后我将这个字符创通过反序列化转化为一颗二叉树。所以我需要不断的判断这个字符创中的字符是否是 #字符。
/*public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; }}*/public class Solution { public int index = -1; String Serialize(TreeNode root) { //序列化主要就是通过将这颗树以一种方式进行顺序化。我们就将这颗二叉树进行先序遍历,得到 //访问这颗二叉树的一个访问顺序 StringBuffer sb = new StringBuffer(); if(root == null){ sb.append("#,"); return sb.toString(); } sb.append(root.val+","); sb.append(Serialize(root.left)); sb.append(Serialize(root.right)); return sb.toString(); } TreeNode Deserialize(String str) { //反序列化二叉树则是需要将一个二叉树组成的字符创进行反序列化,然后得到这个 //字符串表示的二叉树 index ++; int length = str.length(); if(index >= lengt mnh){ return null; } String[] strr = str.split(","); TreeNode node = null; while(!strr[index].equals("#")){ node = new TreeNode(Integer.valueOf(strr[index])); node.left = Deserialize(str); node.right = Deserialize(str); } return node; }}
题目9:已知两个数组,数组里的元素有正有负,但是都是按照从小到大已经排好序,要求用尽可能小的时间复杂度编写一算法求出两个数组的最大交集。
例如:
A[] = {-10,5, 6},
B[] = {2, 4, 5, 6, 12};
那么交集为:{5, 6}
顺序取出数组 A 中的每个元素,然后对取出的每个元素在数组 B 中进行二分查找,如果没有找到,一直到结束,如果找到,假如找到的元素在 B 中的 Index 为 J,在 A 中的 Index 为 I,那么从 I+1,J+1 开始对两个数组进行取元素并进行比较,如果相等则一直继续,如果不相等,那么从 Index I 到当前 Index-1 就是两个数组的最大交集.
#include "stdafx.h"
#include
int FindValue(const int *pB, int i32FirIdx, int i32EndIdx, int nValue){
assert(NULL != pB);
if (i32FirIdx > i32EndIdx) return -1;
int i32MidIdx = (i32FirIdx + i32EndIdx) / 2;
if (pB[i32MidIdx] > nValue)
{
return FindValue(pB, i32FirIdx, i32MidIdx - 1, nValue);
}
else if (pB[i32MidIdx] < nValue)
{
return FindValue(pB, i32MidIdx + 1, i32EndIdx, nValue);
}
return i32MidIdx;
}
bool GetMaxSubArray(const int *pA, int nASize, const int *pB, int nBSize){
assert(NULL != pA || NULL != pB);
assert(0 <= nASize || 0 <= nBSize);
int i32Idx = -1;
int i32I = 0, i32J = 0;
for (i32I = 0; i32I < nASize; i32I++)
{
if ((i32Idx = FindValue(pB, 0, nBSize - 1, pA[i32I])) != -1)
{
printf("Max sub array is : %d ", pA[i32I]);
for (i32I++, i32J = i32Idx + 1; i32I < nASize && i32J < nBSize; i32I++, i32J++)
{
if (pA[i32I] == pB[i32J])
{
printf("%d ", pA[i32I]);
}
}
return true;
}
}
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
int aryA[] = {-10, 5, 6};
int aryB[] = {2, 4, 5, 6, 12};
if (!GetMaxSubArray(aryA, sizeof(aryA)/sizeof(int), aryB, sizeof(aryB)/sizeof(int)))
{
printf("Not sub array!\n");
}
return 0;
}
题目10:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
奇数行从左到右打印,偶数行从右到左打印。那么使用两个栈,一个栈存奇数行(先保存右子树再保存左子树),一个栈存偶数行(先保存左子树再保存右子树)。仔细分析下:第一行是奇数行,只有一个入奇数栈。然后出栈。出站的同时用偶数栈将该结点的左子树保存,再保存右子树,那么这样一来,下次取出偶数栈中的数据在树中看就是从左到右,取出的同时,用奇数栈将该结点的右子树保存,再保存左子树。这样一来,下次取出奇数栈中的数据在树中看就是从右到左。
import java.util.ArrayList;
import java.util.Stack;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList > Print(TreeNode pRoot) {
/*打印二叉树。之字的话。那么我们可以利用两个栈,一个用来存奇数行(先右子树再左子树)
一个用来存放偶数行(先左子树再右子树出站就是右往左)
,*/
int layer = 1;
Stack stack1 = new Stack();
stack1.push(pRoot);
Stack stack2 = new Stack();
ArrayList> lists = new ArrayList>();
while(!stack1.isEmpty() || !stack2.isEmpty()){
if(layer % 2 == 1){
ArrayListlist = new ArrayList();
while(!stack1.isEmpty()){
TreeNode node = stack1.pop();
if(node != null){
list.add(node.val);
System.out.print(node.val + " ");
stack2.push(node.left);//因为放这个左节点是因为之后下一层是偶数层,需要从右往左
stack2.push(node.right);
}
}
if(!list.isEmpty()){
lists.add(list);
layer ++;
System.out.println();
}
}else{
ArrayListlist = new ArrayList();
while(!stack2.isEmpty()){
TreeNode node = stack2.pop();
if(node != null){
list.add(node.val);
System.out.print(node.val + " ");
stack1.push(node.right);
stack1.push(node.left);
}
}
if(!list.isEmpty()){
lists.add(list);
layer ++;
System.out.println();
}
}
}
return lists;
}
}
计算机视觉常见面试题型介绍及解答
第一期 | 第二期 | 第三期 | 第四期 | 第五期 |
第六期 | 第七期 | 第八期 | 第九期 | 第十期 | 第11期
腾讯算法工程师笔试真题介绍及解析汇总合集
第一期 | 第二期 | 第三期 | 第四期 | 第五期
阿里算法岗位最新编程题介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期
第六期 | 第七期
华为研发工程师编程题型介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期 |
第六期 | 第七期 | 第八期 | 第九期 | 第十期 |
字节跳动校招研发岗位笔试编程题型介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期
第六期 | 第七期 | 第八期 | 第九期 | 第十期
网易算法工程师笔试编程题型介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期
第六期 | 第七期 | 第八期 | 第九期 | 第十期
NLP 精华面试专题介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期 | 第六期
百度数据结构 / 算法面试题型介绍及解析
第一期 | 第二期 | 第三期 | 第四期 | 第五期
第六期