我很喜欢那些javascript解决的编程题,感觉非常的有意思。我在博客园上面看到了一个同学的博客,他一共发了34篇剑指offer的编程题,还给出了非常详细的解答。

接下来的工作,我做的就是搬运工,不论是布局,还是内容的呈现,还是我对问题的解答。肯定是没有原创者厉害的,但是,我借鉴,甚至是完全照搬他的内容,让我学习了很多东西。

有些东西我可能现在都有问题,比如链表的那几道中有两道我确实懵逼了,甚至,我只是觉得,程序只是数学的逻辑体现。

这个同学博客的链接地址是 echoVic:http://www.cnblogs.com/echovic/

//链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接

//次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。

//每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

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

function ListNode(x){

this.val=x;

this.next=null;

}

function printListFromTailToHead(head){

var res=[];

while(head){

res.unshift(head.val);

head=head.next;

}

return res;

}



//斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、

//……在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N

//方法1:递归

function Fibonacci(n){

switch(n){

case "0":

return 1;

break;

case "1":

return 1;

break;

default:

Fibonacci(n-1)+Fibonacci(n-1);

break;

}

}

alert(Fibonacci(6));



//二维数组中的查找

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

function Find(target,array){

var i=array.length-1;

var j=0;

while(i>=0&&array[i][j]){

if(array[i][j]<target){

j++;

}else if(array[i][j]>target){

i--;

}else{

return true;

}

}

return false;

}



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

function replaceSpace(str){

return str.replace(/\s/g,'20%');//把空格用20%代替

}



//从尾到头打印链表

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

function printListFromTailToHead(head){

var res=[];

while(head){

res.unshift(head.val);

head=head.next;

}

return res;

}



function LinkedList(){

var node=function(element){

this.element=element;

this.next=null;

};

var length=0;

var head=null;

}



this.append=function(element){

var node=new Node(element),

current;

if(head=null){

head=node;

}else{

current=head;

while(current.next){

current=current.next;

}

current.next=node;

}

length++;//更新列表的长度

}



this.removeAt=function(position){

if(position>-1&&position<length){

var current=head,

previous,

index=0;

if(position=0){

head=current.next;

}else{

while(index++<position){

previous=current;

current=current.next;

}

previous.next=current.next;

}

length--;

return current.element;

}else{

return null;

}

}



// 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。

//假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

///例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

function reConstructBinaryTree(pre,vin){

if(!pre||pre.length=0){

return;

}

var treeNode={

val:pre[0]

}

for(var i=0;i<pre.length;i++){

if(vin[i]=pre[0]){

treeNode.left=reConstructBinaryTree(pre.slice(1,i+1),vin.slice(0,i));

treeNode.right=reConstructBinaryTree(pre.slice(i+1),vin.slice(i+1));

}

}

return treeNode;

}



function BinarySearchTree(){

var Node=function(key){

this.key=key;

this.left=null;

this.right=right;

};

var root=null;

}

this.insert=function(key){

var newNode=new Node(key);

if(root=null){

root=newNode;

}else{

insertNode(root.newNode);

}

}

this.search=function(key){

return searchNode(root,key);

};

var searchNode=function(node,key){

if(node=null){

return false;

}

if(key<node.key){

return searchNode(node.left,key);

}else if(key>node.key){

return searchNode(node.right,key);

}else{

return true;

}

}

this.remove=function(key){

root=removeNode(root,key);

}



//旋转数组的最小数字

//把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

  //输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

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

function minNumberInRotateArray(rotateArray){

var len=rotateArray.length;

if(len0){

return 0;

}else{

var low=0;

var high=len-1;

while(low<len-1){

var mid=low+Math.floor((high-low)/2);

if(roateArray[mid]>roateArray[high]){

low=mid+1;

}else if(rotateArray[mid]rotateArray[high]){

high=high-1;

}else{

high=mid;

}

}

return rotateArray[low];

}

}



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

//本题的前提是只有一次1阶或者2阶的跳法:

//假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1);

//假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2);

//由假设得出总跳法为:f(n)=f(n-1)+f(n-2);

//当台阶只有一阶时,f(1)=1,只有两阶时时,f(2)=2;

//到这大家估计都看出来了,最终得出的是一个斐波那契数列:

//n=1, f(n)=1

//n=2, f(n)=2

//n>2,且为整数, f(n)=f(n-1)+f(n-2)

function jumpFloor(number){

if(number<0){

return -1;

}else if(number<=2){

return number;

}

var arr=[];

arr[0]=1;

arr[1]=2;

for(var i=2;i<number;i++){

arr[i]=arr[i-1]+arr[i-2];

}

return arr[number-1];

}



//变态跳台阶

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

//延续前一篇文章的思路:

//假定第一次跳的是n阶,那么剩下的是0个台阶,跳法是f(0)=1;

//假定第一次跳的是(n-1)阶,那么剩下的是1个台阶,跳法是f(1)=1;

... ...

//假定第一次跳的是1阶,那么剩下的是(n-1)个台阶,跳法是f(n-1);

//以此类推, 由假设得出总跳法为:f(n)=f(n-1)+f(n-2)+···+f(1)+f(0);

//由于f(n-1)=f(0)+f(1)+···f(n-2),

//因此f(n)=(f(0)+f(1)+···f(n-2))+f(n-1)=f(n-1)+f(n-1);

//由此可得

//n=1, f(n)=1

//n>1,且为整数, f(n)=2
f(n-1)

function jupmFloor(number){

if(number<0){

return -1;

}else if(number<=2){

return number;

}

var arr=[];

arr[0]=1;

arr[1]=1;

for(var i=2;i<=number;i++){

arr[i]=2arr[i-1];

}

return arr[number];

}



//每个台阶都有跳与不跳两种情况(除了最后一个台阶),最后一个台阶必须跳。所以共用2^(n-1)中情况

function jumpFloor(number){

if(number=0){

return -1;

}else{

return Math.pow(2,number-1);

}

}



//由此可得:

//当第一次横着覆盖时,覆盖方法为f(n-2);

//当第一次竖着覆盖时,覆盖方法为f(n-1);

//因此f(n)=f(n-1)+f(n-2);

//当n=1时,只有1种覆盖方法,当n=2时,有2种覆盖方法。

//此题最终得出的仍然是一个斐波那契数列。

n=1, f(n)=1

n=2, f(n)=2

n>2,且为整数, f(n)=f(n-1)+f(n-2)

function jumpFloor(number){

if(number<0){

return -1;

}else if(number<=2){

return number;

}

var arr=[];

arr[0]=1;

arr[1]=2;

for(var i=2;i<number;i++){

arr[i]=arr[i-1]+arr[i-2];

}

return arr[number-1];

}



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

用1和n进行位运算,结果为1则n的二进制最右边一位为1,否则为0;

将n二进制形式右移1位,继续与1进行位运算;

由于负数右移时最高位补1,因此不能采用算术右移,而使用不考虑符号位的逻辑右移。

function Number0f1(n){

var count=0;

while(n!=0){

if((n&1)1){

count++;

}

n=n>>>1;

}

return count;

}



//用1和n进行位运算,结果为1则n的二进制最右边一位为1,否则为0;

//将1左移一位继续进行位运算,直到左移32位截至。

function Number0f1(n){

if(n=0){

return 0;

}

var count=0,

flag=1;

while(flag){

if(n&flag){

count++;

}

flag=flag<<1;

console.log(flag);

}

return count;

}



//如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响;

//例如:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011;

//我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000;

//因此,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就将进行多少次这样的操作。

function Number0f1(n){

var count=1;

while(n!=0){

++count;

n=(n-1)&n;

}

return count;

}



//调整数组顺序使奇数位于偶数前面

//输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所

//有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

//新建两个数组,分别用来存放奇数和偶数;

//将偶数的数组连接到奇数数组后面。

function reOrderArray(array){

var odd=[];

var even=[];

for(var i=0;i<array.length;i++){

if(array[i]%2=0){

even.push(array[i]);

}else{

odd.push(array[i]);

}

}

return odd.concat(even);

}

//push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。

//concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。



//反转链表

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

//pHead为当前结点,如果当前结点为空的话,直接返回;

//pHead为当前结点,pre为当前结点的前一个结点,next为当前结点的下一个结点;

//需要完成的目标是将pre-->pHead-->next1-->next2-->··· ···-->end反转为pre<--pHead<--next1<--next2<--··· ···<--end;

//pre结点可以用来反转方向,为了避免反转之后链表断开,用next结点暂时保存next1结点;

//先用next保存pHead的下一个结点信息,保证单链表不会断裂;

//保存之后,让pHead从指向next变成指向pre;

//到此,完成了pre到pHead的反转,即pre<--pHead;

//将pre,pHead,next依次向后移动一个结点。

//循环操作,直到pHead为null,此时pre就是链表的最后一个结点,链表反转完毕,pre为反转后链表的第一个结点。

//输出pre就是反转之后所得的链表。

function isEmptyobject(obj){

for(var name in obj){

return false;

}

return true;

}

function ReverseList(pHead){

if(isEmptyobject(pHead)){

return false;

}

var pre=null;

var next=null;

while(pHead!=null){

next=pHead.next;

pHead.next=pre;

pHead=next;

}

return pre;

}



//树的子结构

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

//叉树子结构的意思是包含了一个结点,可以只取左子树或者右子树,或者都不取。例如:

//由于A中有一部分子树的结构和B是一样的,因此B是A的子结构。

//有关二叉树的算法问题,一般都可以通过递归来解决。那么写成一个正确的递归程序,首先一定要分析正确递归结束的条件。

//如果根节点相同则递归调用isSubtree(),如果根节点不相同,则判断root1的左子树和root2是否相同,再判断右子树和tree2是否相同;

//注意null的条件,HasSubTree中,如果两棵树都不为空才进行判断,isSubtree中,如果root2为空,则说明第二棵树遍历完了,即匹配成功;

//root1为空有两种情况:(1)如果root1为空&&root2不为空说明不匹配,(2)如果root1为空,root2为空,说明匹配。

function treeNode(x){

this.val=x;

this.left=null;

this.right=null;

}

function isSubtree(root1,root2){

if(root2null)return true;

if(root1null)return false;

if(root1.valroot2.val){

return isSubtree(root1.left,root2.left)&&isSubtree(root1.right,root2.right);

}else{

return false;

}

}

function HasSubTree(pRoot1,pRoot2){

if(pRoot1null||pRoot2null){

return false;

}

return isSubtree(pRoot1,pRoot2)||HasSubtree(pRoot1.left,pRoot2)||

HasSubTree(pRoot1.right,pRoot2);

}



//二叉树的镜像

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

//有关二叉树的算法问题,一般都可以通过递归来解决。那么写一个正确的递归程序,首先一定要分析正确递归结束的条件。

//先前序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换它的两个子节点;

//当交换完所有的非叶子结点的左右子结点之后,就得到了树的镜像

function Mirror(root){

if(root=null){

return null;

}

var temp=root.left;

var root.right=temp;

Mirror(root.left);

Mirror(root.right);

}



//顺时针打印矩阵

//输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵:

//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.

//选坐标为(0,0),(1,1)...的点记为(start,start),作为开始坐标,下一圈开始坐标为(start+1,start+1);

//判断是否进入下一圈(即是否打印完成)的条件是rows>start
2 && cols>start2;

//打印一圈的左上角坐标为(start,start),右下角的坐标为(cols-start-1,rows-start-1)

//根据一圈左上角和右下角坐标判断“从左到右”,“从上到下”,“从右到左”,“从下到上”需要打印的点。

function printMatrix(matrix){

if(matrixnull||matrix.length0){

return;

}

var rows=matrix.length;

var cols=matrix[0].length;

var result=[];

while(cols>start
2&&rows>start2){

var endX=cols-1-start;

var endY=rows-1-start;

while(cols>start
2&&rows>start*2){

var endX=cols-1-start;

var endY=rows-1-start;

//从左到右打印一行

for(var i=start;i<=endY;i++){

result.push(matrix[i][endX]);

}

//从上到下打印一行

if(start<endY){

for(start<endY){

for(var i=start+1;i<=endY;i++){

result.push(matrix[i][endX]);

}

}

}

//从右到左打印一行

if(start<endY&&start<endY){

for(var i=endY-1;i>=start;i--){

result.push(matrix[endY][i]);

}

}

//从下到上打印一行

if(start<endX&&start<endY-1){

for(var i=endY-1;i>=start+1;i--){

result.push(matrix[i][start]);

}

}

}

start++;

}

return result;

}



//包含min函数的栈

//定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

var stack=[];

function push(node){

stack.push(node);

}

function pop(){

return stack.pop();

}

function top(){

return stack[0];

}

function min(){

return Math.min.apply(this.stack);

}

module.exports={

push:push,

pop:pop,

top:top,

min:min

}



//栈(stack)又名堆栈,它是一种运算受限的线性表。

//其限制是仅允许在表的一端进行插入和删除运算。

//这一端被称为栈顶,相对地,把另一端称为栈底。

//向一个栈插入新元素又称作进栈、入栈或压栈,

//它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;

//从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,

//使其相邻的元素成为新的栈顶元素。

//栈初始化:创建一个空栈

Init:function(){

this.STACKMAX=99;

this.stack=new Array(this.STACKMAX);

this.top=-1;

return this.stack;

}

//判断栈空: 若栈为空返回true,否则返回false

isEmpty:function(){

if(this.top-1){

return true ;

}else {

return false;

}

}

//进栈:若栈满,返回“栈满”。否则将元素elem作为新的栈顶元素。

Push:function(node){

if(this.topthis.STACKMAX-1){

return new Error("栈满");

}else{

this.top++;

this.stack[this.top]=node;

}

}

//退栈:删除栈顶元素,并返回其值

Pop:function(){

if(this.top-1){

return new Error("空栈,无法删除栈顶的元素");

}else{

return this.stack[this.top-1];

}

}

//读栈顶元素:返回栈顶元素

Top:function(){

if(this.top!=-1){

return this.stack[this.top];

}else{

return new Error("空栈,顶元素无返回值");

}

}

//清空栈:将栈清空为空栈

Clear:function(){

this.top=-1;

}

//栈长度:返回栈的元素个数,既栈的长度

Length:function(){

return this.top+1;

}



//栈的压入、弹出序列

//输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。

//假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,

//但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)。

//入栈:1,2,3,4,5

//出栈:4,5,3,2,1

//1) 1入辅助栈,此时1≠4;

//2) 2入辅助栈,此时2≠4;

//3) 3入辅助栈,此时3≠4;

//4) 4入辅助栈,此时4=4,辅助栈出栈,剩下 1,2,3;同时,弹出序列向后一位,为5;此时3≠5,继续压栈;

//5) 5入辅助栈,此时5=5,辅助栈出栈,剩下1,2,3;同时,弹出序列向后一位,为3;

//6) 此时3=3,辅助栈出栈,剩下1,2;同时弹出序列向后一位,为2;

//7) 此时2=2,辅助栈出栈,剩下1;同时弹出序列向后一位,为1;

//8) 此时1=1,辅助栈出栈,为空,所以该序列是压栈序列对应的一个弹出序列。

function IsPopOrder(pushV,popV){

if(pushV.length=0||popV.length=0){

return ;

}

var temp=[];

var popIndex=0;

for(var i=0;i<pushV.length;i++){

temp.unshift(pushV[i]);

while(temp.length&&temp[0]=popV[popIndex]){

temp.shift();

popIndex++;

}

}

return (temp.length=0);

}

module.exports={

isPopOrder:IsPopOrder

}



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

//借助两个辅助队列,一个用来存放结点,一个用来存放结点值;

//先将根节点加入到队列中,然后遍历队列中的元素,遍历过程中,访问该元素的左右节点,

//再将左右子节点加入到队列中来。

function TreeNode(x){

this.val=x;

this.left=null;

this.right=null;

}

function PrintFromTopToBottom(root){

var queue=[];

queue.push(root);

var result=[];

if(rootnull){

return result;

}

while(queue.length){

var temp=queue.shift();

if(temp.left){

queue.push(temp.left);

}

if(temp.right){

queue.push(temp.right);

}

}

return result;

}



//二叉搜索树的后序遍历序列

//输入一个整数数组,判断该数组

//是不是某二叉搜索树的后序遍历的结果。

//如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都

//互不相同。

//二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)

//它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,

//则左子树上所有结点的值均小于它的根结点的值;

//若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

//它的左、右子树也分别为二叉排序树。

function adjustSequence(sequence,start,end){

if(start>=end){

return true;

}

var i=end;

while(i>start&&sequence[i-1]>sequence[end]){

--i;

}

for(var j=i-1;j>=start;--j){

if(sequence[j]>sequence[end]){

return false;

}

}

return adjustSequence(sequence,start,i-1)&& && (adjustSequence(sequence, i, end - 1));

}

function VerifySquenceOfBST(sequence){

if(!sequence.length){

return false;

}

return adjustSequence(sequence,0,sequence.length-1);

}



//二叉树中和为某一值的路径

//输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

//前序遍历二叉树,每次更新当前路径的和curtSum;

//判断当前结点是否是叶子结点,以及curtSum是否等于expectNumber。如果是,把当前路径保存在res结果中;

//若不符合条件,则弹出此结点

function TreeNode(x){

this.val=x;

this.left=null;

this.right=null;

}

function FindPath(root,expectNumber){

var res=[];

if(root=null){

return result;

}

dfsFind(root,expectNumber,[],0,result);

return result;

}

function dfsFind(root,expectNumber,path,currentSum,result){

currentSum+=root.val;

path.push(root.val);

if(currentSumexpectNumber&&root.leftnull&&root.rightnull){

result.push(path.slice(0));

}

if(root.left!=null){

dfsFind(root.left, expectNumber, path, currentSum, result);

}

if(root.right!=null){

dfsFind(root.right, expectNumber, path, currentSum, result);

}

path.pop(); }



//复杂链表的复制

//输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),

//返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

//递归思想:把大问题转换为若干小问题。

//将复杂链表分为头结点和剩余结点两部分,剩余部分采用递归方法。

function RandomListNode(x){

this.label=x;

this.next=null;

this.random=null;

}

function Clone(pHead){

if(!pHead){

return null;

}

//复制头结点

var node=new RandomListNode(pHead.label);

node.random=pHead.random;

node.next=Clone(pHead.next);

return node;

}



//二叉搜索树与双向链表

//输入一棵二叉搜索树,将该二叉搜索树转换成

//一个排序的双向链表。要求不能创建任何新的结点,

//只能调整树中结点指针的指向。

//递归思想:把大问题转换为若干小问题;

//由于JavaScript中并没有链表或者Tree这样的原生数据结构,都是通过对象模拟的,因此最终要返回的是指向双向链表首结点的指针;

//将左子树构成双向链表,返回的是左子树的尾结点,将其连接到root的左边;

//将右子树构成双向链表,将其追加到root结点之后,并返回尾结点;

//向左遍历返回的链表至头结点处,即为所求双向链表的首结点。

function TreeNode(x){

this.val=x;

this.left=null;

this.right=null;

}

function Convert(pRootofTree){

if(!pRootofTree){

return null;

}

var lastNode=null;

lastNode=ConvertNode(pRootofTree,lastNode);

var head=lastNode;

while(head&&head.left){

head=head.left;

}

return head;

}

function ConvertNode(node,lastNode){

if(!node){

return;

}

if(node.left){

lastNode=ConvertNode(node.left,lastNode);

}

node.left=lastNode;

if(lastNode){

lastNode.right=node;

}

lastNode.right=node;

if(node.right){

lastNode=ConvertNode(node.right,lastNode);

}

return lastNode;

}



//字符串的排列

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

//输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

//递归思想:把大问题转换为若干小问题;

//n个元素的全排列 = (n-1) 个元素全排列 + 一个元素作为前缀。

//递归的出口:只有一个元素的全排列,此时排序完成,输出数组。

//遍历字符串,将每个字符放在第一个元素作为前缀,并将其余元素继续全排列。

//新建一个isRepeat空对象,用来判断字符是否重复,若重复则跳过排序。

function Permutation(str){

var result=[];

if(str.length<=0){

return [];

}

var sortTemp="";

var arr=str.split("");

var result=sortString(arr,sorttemp,[]);

return result;

}

function sortString(arr,sortTemp,res){

if(arr.length0){

res.push(sortTemp);

}else{

var isRepeat={};

for(var i=0;i<arr.length;i++){

if(!isRepeat[arr[i]]){

var temp=arr.splice(i,1)[0];

sortTemp+=temp;

sortString(arr,sortTemp,res);

arr.splice(i,0,temp);

sortTemp=sortTemp.slice(0,sortTemp.length-1);

isRepeat[temp]=true;

}

}

}

return res;

}



//数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

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

//新建一个空对象obj保存数组中数字出现的次数;

//新建一个空对象obj保存数组中数字出现的次数;

//遍历数组,如果该数字出现过,则obj中以该数字为key的value加1;

//若该数字未出现过,则obj中以该数字为key的value设为1;

//遍历obj对象,返回value大于数组长度一半的key,即为所求数字。

function MoreThanHalfNum_Solution(number){

var obj={};

var length=number.length;

numbers.forEach(function(d){

if(obj[d]){

obj[d]++;

}else{

obj[d]=1;

}

});

for(var i in obj){

if(obj[i]>Math.floor(length/2)){

return i;

}

}

return 0;

}



//最小的K个数

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

//思路一使用JavaScript的Array对象的sort()方法进行自小到大排序,然后输出最小的k个数。

function GetLeastNumbers_Solution(input,k){

if(input.length<k)return false;

input.soft(function(a,b){

return a-b;

});

return input.slice(0,k);

}



//利用快速排序的原理解决问题。

//基于数组的第k个数字来调整,使得比第k个数字小得所有数字都位于其左边,比第k个数字大的所有数字都位于数组的右边。

//调整之后,位于数组左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)。

function swap(arr,i,j){

var temp=arr[i];

arr[i]=arr[j];

arr[j]=temp;

}

function partation(arr,start,end){

var pivot=arr[end];

var i=start;

for(var j=start;j<end;j++){

if(arr[j]<pivot){

swap(arr,i,j);

i++;

}

}

swap(arr,i,end);

return i;

}

function GetLeastNumbers_Solution(input,k){

var result=[];

if(input.length<0||k>input.length||k<=0){

return false;

}

var start=0;

var end=input.length-1;

var p=partation(input,start,end);

while (p ! (k - 1)) {

if (p > (k - 1)) {

end = p - 1;

p = partation(input, start, end);

} else {

start = p + 1;

partation(input, start, end);

}

}

for(var i=0;i<k;i++){

result.push(input[i]);

}

return result;

}



//连续子数组的最大和

//HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?

//例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)。

//遍历数组,遇到负的和则放弃之前的结果,重新积累,这期间保留最大值;

//用sum记录最终返回的最大和,用tempsum记录累计值;

//对于数组中的一个数array[i],若其左边的累加和非负,那么加上array[i];

//判断此时的tempsum是否大于sum,若大于此时的sum,则用sum记录下来。

function FindGreastSumOfSubArray(array){

if(array.length<0)return 0;

var sum=array[0],

tempsum=array[0];

for(var i=1;i<array.length;i++){

tempsum=(tempsum<0)?array[i]:tempsum+array[i];

sum=(tempsum>sum)?tempsum:sum;

}

return sum;

}



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

//求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,

//但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。

//设n=abcde,自右至左,从1开始标号。

//如果第i位上的数字为0,则第i位可能出现1的次数由其高位决定,若没有高位,则视为0,此时第i位可能出现1的次数为:其高位数10(i-1),例如若c为0,则次数为ab*102;

//如果第i位上的数字为1,则第i位上可能出现1的次数受其高位和低位影响,若没有,则视为0,此时第i位可能出现1的次数:其高位数
10(i-1)+(低位数+1),例如若c为1,则次数为ab*102+(de+1);

//如果第i位上的数字大于1,则第i位上可能出现1的次数受其高位影响,

//若没有,则视为0,此时第i位可能出现1的次数:(其高位数+1)10(i-1),例如若c大于1,则次数为(ab+1)*102;

function Number0f1Between1AndN_Solution(n){

if(n<0)return 0;

var high,low,cur,temp,i=1;

high=n;

var count=0;

while(high!0){

high=parseInt(n/Math.pow(10,i));

temp=n%Math.pow(10,i);

cur=parseInt(temp/Math.pow(10,i-1));

low=temp%Math.pow(10,i-1);

if(cur=1){

count+=high
Math.pow(10,i-1);

}else{

console.log(count,high);

count+=(high+1)*Math.pow(10,i-1);

}

i++;
	}
return count;
}</pre></code><pre><code>

//把数组排成最小的数

//输入一个正整数数组,把数组里所有数字拼接起来排成一个数,

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

//本题关键点是制定排序规则,设计比较器;

//排序规则如下:

//若ab > ba 则 a > b,

//若ab < ba 则 a < b,

//若ab = ba 则 a = b;

//例如:比较3和31时,'331' > '313',所以返回结果是'3' > '31'。

//根据指定排序规则对数组进行排序,然后从小到大拼接即为所求结果。

function Comparator(a,b){

var s1=a+""+b;

var s2=b+""+a;

for(var i=0;i<s1.length;i++){

if(s1.CharAt(i)>s2.charAt(i)){

return 1;

}

if(s1.charAt(i)<s2.charAt(i)){

return -1;

}

}

return 1;

}

function PrintMinNumber(numbers){

numbers.sort(Comparator);

var result="";

for(var i=0;i<numbers.length;i++){

result=result+numbers[i];

}

return result;

}



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

//按顺序将丑数保存在数组中,然后求下一个丑数;

//下一个丑数是由数组中某个丑数A * 2,B * 3,C * 5中的最小值得来的。

//按照题目规定,第一个丑数是1,存入数组中;

//第二个丑数为12,13,15三个中的最小值;

//第三个丑数为2
2,13,15三个中的最小值,依次类推,求出第N个数组。

function getUglyNumber_Solution(index){

if(index0)return 0;

var uglyNum=[1];

var factor2=0;

factor3=0;

factor5=0;

for(var i=1;i<index;i++){

uglyNum[i]=Math.min(uglyNum[factor2]2,uglyNum[factor3]3,uglyNum[factor5]*5);

if(uglyNum[i]=uglyNum[factor2]2)factor2++;

if(uglyNum[i]===uglyNum[factor3]
3)factor3++;

if(uglyNum[i]=uglyNum[factor5]*5)factor5++;

}

}



//第一个只出现一次的字符

//在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符,并返回它的位置。

//新建一个对象,其中key用来存放字符,value用来存放该字符出现的次数;

//第一次循环,将所有字符和对应出现的次数存放在map中,时间复杂度为0(n);

//第二次循环找到value为1的字符所在的位置,并返回。

function FirstNotRepeationChar(str){

if(str.length0){

return -1;

var map={};

for(var i=0;i<str.length;i++){

var charX=str[i];

if(!map[charX]){

map[charX]=1;

}else{

map[charX]++;

}

}

for(var i=0;i<str.length;i++){

var charY=str[i];

if(map[charY]==1){

return i;

}

}

}

}

关于数据结构,剑指offer上面的的更多相关文章

  1. 【剑指offer】从上向下打印二叉树

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26089165 剑指offer上的第23题,实际上就是考察二叉树的层序遍历,详细思想能够參考 ...

  2. 【剑指offer】包括min函数的栈

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26064213 剑指offer上的第21题,之前在Cracking the Coding i ...

  3. 【剑指offer】扑克牌的顺子

    个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想測測自己的手气,看看能不能抽到顺子,假设抽到的话,他决定去买体育彩票,嘿嘿! ."红心A,黑桃3,小王,大王,方片 ...

  4. 【剑指offer】斐波那契序列与跳台阶

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/25337983 剑指offer上的第9题,简单题,在九度OJ上測试通过. 主要注意下面几点: ...

  5. 【剑指offer】直扑克

    个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想測測自己的手气,看看能不能抽到顺子,假设抽到的话,他决定去买体育彩票,嘿嘿! ! "红心A,黑桃3,小王,大王,方 ...

  6. 剑指offer

    今天完成了剑指offer上的66道编程题,感觉自己还是很多代码实现能力和算法积累都还不够!还需要继续联系,坚持自己独立写代码实现. 最后将今天的两道题目奉上,都有异曲同工之妙: 矩阵中的路径: #in ...

  7. 【剑指offer】顺时针打印矩阵

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26053049 剑指offer上的第20题,九度OJ上測试通过. 题目描写叙述: 输入一个矩 ...

  8. 【剑指offer】近期公共祖先

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/28113959 剑指offer上的最后一题了,一个递归函数调了一下午,才得到正确的结果. 题 ...

  9. 【剑指offer】八皇后问题

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26614999 剑指offer上解决八皇后问题,没实用传统的递归或非递归回溯法,而是用了非常 ...

随机推荐

  1. 当mysql报错1045时的解决方法

    2.用记事本打开 添加 打开后,搜索mysqld关键字 找到后,在mysqld下面添加skip-grant-tables,保存退出. 如果保存在了c盘里不能修改那么就采用这样的方法 然后就可以修改c盘 ...

  2. AspectJ用注解替换xml配置

    AspectJ基于注解的使用 AspectJ简介 AspectJ是一个基于Java语言的AOP框架,一般 其主要用途:自定义开发 一般情况下spring自动生成代理,要配置aop, 首先确定目标类,a ...

  3. 在编写wpf界面时候中出现如下错误: 类型引用不明确。至少有两个名称空间(“System.Windows”和“System.Windows”)中已出现名为“VisualStateManager”的类型。请考虑调整程序集 XmlnsDefinition 特性。

    wpf中类型引用不明确.至少有两个名称空间(“System.Windows”和“System.Windows”)中已出现名为“VisualState 你是不是用了WPFToolKit?如果是的,那原因 ...

  4. macOS & USB stick

    macOS & USB stick why macOS can only read USB stick, can not write files to USB stick macos 无法写文 ...

  5. windows 10 multi virtual desktop keyboard shortcut

    windows 10 multi virtual desktop keyboard shortcut windows 10 multi desktop keyboard shortcut https: ...

  6. Yii2总结

    1. Web访问流程(即在浏览器中输入一个网址至浏览器展现页面结果的过程) a. 将输入的网址提取出域名,在本地hosts文件中查找对应的IP地址(windows为C:/windows/system3 ...

  7. 老男孩python学习自修第十七天【装饰器】

    装饰器:在某个方法执行前后去执行其他新定义的行为 例如: #!/usr/bin/env python # _*_ coding:UTF-8 _*_ def before_say_hello(): pr ...

  8. Jenkins+PowerShell持续集成环境搭建(七)构建触发器

    Jenkins 有三种类型的构建触发器,如下图: Build after other projects are built:在其他项目构建后构建: Build periodically:定时构建: P ...

  9. debug错误

    Description "opt_design" can fail with error messages similar to the following: opt_design ...

  10. Nginx 反向代理接收用户包体方式

    陶辉91课 如果proxy_request_buffering 设置为on的时候是等待nginx读取完包体后再发送上游服务器 一般依赖于nginx处理能力  client_body_in_file_o ...