关于数据结构,剑指offer上面的

我很喜欢那些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(root1
null)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(pRoot1
null||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.top
this.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(currentSum
expectNumber&&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;
}
}
}
}

原文地址:https://www.cnblogs.com/smart-girl/p/7242793.html