问题1:给定一个数组a和一个长度值l,生成一个指定长度值为l的新数组b,要求b中的每一个元素都是从a中随机获取的,且数组元素不可重复。
function generate(a,l){
if(a.length < l) return '老数组长度不足!';
/*a长度可能超出,可以生成新数组后截取长度为l的赋值给b,也可以生成为l的b(这里选第二种)*/
var i=0, tmp=[], b=[];
// 每次获取a的一个随机元素,同时将
while(i<l && tmp.length<=a.length) {
var random = Math.floor(Math.random()*(a.length));
if(tmp.indexOf(random) == -1) { // 判断随机元素是否已经使用过,未使用则放入临时数组记录
tmp.push(random);
var number = a[random]; // 获取数组a中的随机数
console.log(random+"..."+number);
if(b.indexOf(number) == -1) { //部分IE不支持indexOf请自行解决。也可以使用jquery的$.inArray方法,总之就是不重复扔进b
b.push(number);
i++; // 新随机数时+1
}
}
}
if(b.length==l) return b;
else return '新数组长度不足: '+ b;
}
// 测试
var a=[3,2,1,6,3,9,2,5];
generate(a, 5)
问题2:使用js写一个栈结构。然后如何让这个栈满足两个调用者同时使用的要求?
function Stack() {
this.dataStore = [];
this.top = 0;//栈顶元素的位置
this.push = push;
this.pop = pop;
this.peek = peek;
this.length=length;
this.clear=clear;
}
function push(element) {//进栈
this.dataStore[this.top++] = element;
}
function pop() {//出栈
return this.dataStore[--this.top];
}
function peek() {//栈顶元素
return this.dataStore[this.top-1];
}
function length() {
return this.top;
}
function clear() {
this.top = 0;
}
//测试栈的实现
var s= new Stack();
s.push("D");
s.push("R");
s.push("B");
console.log(s.length());
console.log(s.peek());
console.log(s.clear());
console.log(s.length());
问题3:找出数组中只出现一次的元素
function num(arr){
var str=arr.join('');
var res=[];
for(var i=0;i<str.length;i++){
var num=str.split(str[i]).length-1;
if(num===1){
res.push(str[i]);
}
}
return res;
}
var arr=[1,2,1,4,3,3];
console.log(num(arr));
问题4:现在对于给定的一个字符串s,请计算出字典序最大的s的子序列。
function max(str){
//字典序从小到大:abcdefg......
var arr=str.split('');
var res=[arr[arr.length-1]];//字符串最后一个元素肯定符合条件
for(var i=arr.length-2;i>=0;i--){//从倒数第二个元素开始,找单调递增的元素
if(arr[i]>=res[0]){
res.unshift(arr[i]);//找到符合的直接加入到res中
}
}
return res.join('');
}
var str='abddaa';
console.log(max(str));
问题6:把一棵二叉树变成它的镜像二叉树
function Mirror(root){
if(root==null){//当一开始根节是空的时候,返回false
return false;
}
if(root.left ==null && root.right ==null){//当只有一个根节点时返回false
return false;
}
//第三种情况:当左右子树不为空的时候,交互左右子树节点
var temp=root.left;
root.left=root.right;
root.right=temp;
if(root.left!=null){//递归遍历左子树
Mirror(root.left);
}
if(root.right!=null){//递归遍历右子树
Mirror(root.right);
}
}
问题7:顺时针打印矩阵
function printMatrix(matrix)
{
// write code here
var row1 = 0;
var row2 = matrix.length-1;
var col1 = 0;
var col2 = matrix[0].length-1;
var rowflag = 1;
var colflag = 1;
var list=[];
while(row2>=row1 && col2>=col1){
if(rowflag == 1){
for(var i=col1; i<=col2; i++)
list.push(matrix[row1][i]);
row1++;
}
if(colflag == 1){
for(var i=row1; i<=row2; i++)
list.push(matrix[i][col2]);
col2--;
}
if(rowflag == -1){
for(var i=col2; i>=col1; i--)
list.push(matrix[row2][i]);
row2--;
}
if(colflag == -1){
for(var i=row2; i>=row1; i--)
list.push(matrix[i][col1]);
col1++;
}
rowflag = rowflag*(-1);
colflag = colflag*(-1);
}
return list;
}
问题8:Fibinacc数列
function Fibnacci(n){
var res=[0,1];
if(n<2){
return res[n];
}
var fib1=0;
var fib2=1;
var fibn=0;
for(var i=2;i<=n;i++){
fibn=fib1+fib2;
fib1=fib2;
fib2=fibn;
}
return fibn;
}
function jumpFloor(number)
{
if(number ===0){
return 0;
}
if(number===1){
return 1;
}
if(number ===2){
return 2;
}
var one = 1;
var two = 2;
var num = 0;
for(let i=3;i<=number;i++){
num = one + two;
one = two;
two = num;
}
return num
}
问题9:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
function Find(target, array) {
//从最右上角开始确定一个点开始比较
let i = 0;
let j = array[i].length - 1;//将最右边的列作为定点
while (i < array.length && j >= 0) {
if (array[i][j] < target) {
i++;
} else if (array[i][j] > target) {
j--;
} else {
return true;
}
}
return false;
}
问题10:数组中出现次数超过一半的元素
方法1:
function MoreThanHalfNum_Solution(numbers)
{
// write code here
if(numbers==null){
return null;
}
var string=numbers.join('');
var len=Math.floor(numbers.length/2);
for(var i=0;i<numbers.length;i++){
var num=string.split(numbers[i]).length-1;
if(num>len){
return numbers[i];
break;
}
}
return 0;
}
方法2:
function MoreThanHalfNum_Solution(numbers)
{
// write code here
var len=numbers.length;
if(len==0)return 0;
var num=numbers[0];
count=1;
for(var i=0;i<len;i++){
if(num==numbers[i]){
count++;
}else{
count--;
}
if(count==0){
num=numbers[i];
count=1;
}
}
count=0;
for(var i=0;i<len;i++){
if(numbers[i]==num)count++;
}
if(count*2>len)return num;
return 0;
}
问题11:从尾到头打印链表
//借助两个数组arr1和arr2,arr1用来保存按链表顺序压栈,arr2用来保存arr1出栈的元素序列
function printListFromTailToHead(head)
{
// write code here
if(head==null){
return [];
}
var arr1=[];
var arr2=[];
var p=head;
while(p!=null){//首先将元素压入栈
arr1.push(p.val);
p=p.next;
};
while(arr1.length>0){//将arr1里面的元素依次pop到arr2中
arr2.push(arr1.pop());
}
return arr2;
}
问题12:重建二叉树
function reConstructBinaryTree(pre, vin)
{
// write code here
var root=pre[0];
if(pre.length==0||vin.length==0){
return false;
}
for(var i=0;i<vin.length-1;i++){
if(vin[i]===pre[0]){
//找左子树的前序序列和中序序列
var leftPre=[].concat(pre.splice(1,i+1));
var leftIn=[].concat(vin.splice(0,i));
//递归遍历左子树,并将左子树加到根节点上
root.left=reConstructBinaryTree(leftPre,leftIn);
//找右子树的前序序列和中序序列
var rightPre=[].concat(pre.splice(pre.length-i,pre.length));
var rightIn=[].concat(vin.splice(i+1,vin.length));
//递归遍历右子树,并将右子树加到根节点上
root.right=reConstructBinaryTree(rightPre,rightIn);
}
}
return root;
}
问题13:旋转数组的最小数字
function minNumberInRotateArray(rotateArray)
{
// write code here
if (array == null || array.length == 0) {//当输入的是0或者是空的时候
return 0;
}
if (array.length == 1 || array[array.length - 1] > array[0]) {
//当输入的是一个元素或者是单调递增的
return array[0];
}
var start = 0;
var end = array.length - 1;
while (start < end) {
var mid = parseInt((end - start) / 2 + start);
if (array[mid] == array[end]) {
end--;
} else if (array[mid] < array[end]){
end = mid;
} else {
start = mid + 1;
}
}
return array[start];
}
问题14:输入一个链表,输出该链表中倒数第k个结点。
方法一:
function FindKthToTail(head, k)
{
// write code here
if(head==null){
return null;
}
var res=[];
var len=0;
var p=head;
while(p!=null){
res.push(p);
p=p.next;
}
return res[res.length-k];
}
方法二:
function FindKthToTail(head, k) {
// write code here
if( head==null|| k==0 ){
return false;
}
var p1=head;
var p2=null;
for( var i=0;i<k-1;i++){
if(p1.next!=null){
p1=p1.next;
}else{
return null;
}
}
p2=head;
while( p1.next!=null){
p1=p1.next;
p2=p2.next;
}
return p2;
}
问题15:反转链表
/*function ListNode(x){
this.val = x;
this.next = null;
}*/
function ReverseList(pHead)
{
// write code here
if(pHead==null){
return null;
}
var arr=[];
var p1=pHead;
while(p1!=null){
arr.push(p1);
p1=p1.next;
//数组里面装的是节点类型的元素
}
//console.log(arr.length);
var p2Head=arr.pop();
var p2=p2Head;
var pre=p2Head;//用来重新连接的指针
while(arr.length>0){
p2=arr.pop();
pre.next=p2;
pre=p2;
}
pre.next=null;//最后给最后一个元素的next置为空
return p2Head;
}
module.exports = {
ReverseList : ReverseList
};
问题16:合并两个有序链表
function ListNode(x){
this.val = x;
this.next = null;
}
function Merge(pHead1, pHead2)
{
// write code here
var p1=pHead1;
var p2=pHead2;
var pHead3=new ListNode(-1);//自己定义一个头结点
var p3=pHead3;
while(p1!=null && p2!=null){
if(p1.val<=p2.val){
p3.next=p1;
p3=p1;
p1=p1.next;
continue;//如果走到最后一个,不能再进行下面的判断,因为没有val值
}
if(p1.val>p2.val){
p3.next=p2;
p3=p2;
p2=p2.next;
}
}
while(p1!=null){
p3.next=p1;
p3=p1;
p1=p1.next;
}
while(p2!=null){
p3.next=p2;
p3=p2;
p2=p2.next;
}
return pHead3.next;
}
module.exports = {
Merge : Merge
};
问题17:操作给定的二叉树,将其变换为源二叉树的镜像。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function Mirror(root)
{
// write code here
if(root==null){
return null;
}
if(root.left==null&&root.right==null){
return root;
}
//前序遍历 用后序遍历也可以
var temp=root.left;
root.left=root.right;
root.right=temp;
Mirror(root.left);
Mirror(root.right);
}
问题18:从上往下层次打印二叉树节点
解题思路:利用一个队列进行层次遍历,先把一个节点放到队列中,然后再利用这个结点把它的左子结点和右子结点放到队列中;然后再把该节点放到打印数组中;循环的条件是队列不为空
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function PrintFromTopToBottom(root)
{
// write code here
// write code here层序遍历,是先判断元素的左右子树,再将头节点出队列
var queue=[];
var res=[];
if(root!=null){//先判断第一个不为空时把节点加入到队列中
queue.push(root);
}
while(queue.length!=0){//开始判断一下队列的元素
var node=queue.shift();
if(node.left!=null){//先判断下有没有左右子树,有的话先把左右子树节点加入到队列
queue.push(node.left);
}
if(node.right!=null){
queue.push(node.right);
}
res.push(node.val);
}
return res;
}
module.exports = {
PrintFromTopToBottom : PrintFromTopToBottom
};
问题19 :和为S的两个数字
function FindNumbersWithSum(array, sum)
{
// write code here
if(array==null){
return null;
}
var left=0;
var right=array.length-1;
var curNum=0;
var mulSum;
var result=[];
var min=Number.POSITIVE_INFINITY;
var index1;
var index2;
var flag=false;
while(left<right){
curNum=array[left]+array[right];
if(curNum==sum){
flag=true;
mulSum=array[left]*array[right];
if(min>mulSum){
min=mulSum;
index1=left;
index2=right;
}
left++;
right--;
}
if(curNum<sum){
left++;
}
if(curNum>sum){
right--;
}
}
if(flag==true){
result.push(array[index1]);
result.push(array[index2]);
return result;
}
return [];
}
问题20:和为S的连续正数子序列
function FindNumbersWithSum(array, sum)
{
// write code here
if(array==null){
return null;
}
var left=0;
var right=array.length-1;
var curNum=0;
var mulSum;
var result=[];
var min=Number.POSITIVE_INFINITY;
var index1;
var index2;
var flag=false;
while(left<right){
curNum=array[left]+array[right];
if(curNum==sum){
flag=true;
mulSum=array[left]*array[right];
if(min>mulSum){
min=mulSum;
index1=left;
index2=right;
}
left++;
right--;
}
if(curNum<sum){
left++;
}
if(curNum>sum){
right--;
}
}
if(flag==true){
result.push(array[index1]);
result.push(array[index2]);
return result;
}
return [];
}
问题21:将数组按层次展开
var list = [1,2,[3,4],[5,6,[7,8],9],10,11]
var res = [];
function flatten(list, depth){
if(depth == 0){
for(var i = 0; i<list.length; i++){
res.push(list[i]);
}
return;
}
for(var i = 0; i<list.length; i++){
if(list[i] instanceof Array){
flatten(list[i], depth - 1);
}else{
res.push(list[i]);
}
}
return res;
}
console.log(flatten(list, 1));
res = [];
console.log(flatten(list, 2));
问题22:数组去重
方法1:先排序,然后将第一个元素先放进结果数组中,从第二个起循环遍历数组,每次都和结果数组的最后一个元素比较。
function del(arr){
var arr=arr.sort();
var res=[arr[0]];//把第一个元素取出来
for(var i=0;i<arr.length;i++){
if(arr[i]!=res[res.length-1]){//每次都和结果数组中的元素作比较
res.push(arr[i]);
}
}
return res;
}
var arr=[1,3,2,4,2,5,2,5];
console.log(del(arr));
方法2:使用indexof方法
function del(arr){
var res=[];
for(var i=0;i<arr.length;i++){
if(res.indexOf(arr[i])==-1){
res.push(arr[i]);
}
}
return res;
}
方法3:使用set()方法
function quchong(arr){
const s=new Set(arr);
console.log(s);
}
var arr=[1,3,2,4,2,5,2,5];
quchong(arr);
问题23:判断单链表里是否有环,以及其入口节点
1判断是否有环
<script type="text/javascript">
const slow=pHead;//慢指针走一步
const fast=pHead;//
while(slow!=null&fast.next!=null){//没有环的时候,快指针有可能跳出链表,所以让fast的next不为空
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return false;
</script>
2,找环的入口
原理:从链表起点head开始到入口点的距离a,与从slow和fast的相遇点到入口点的距离相等。(别人推导出来的)
<script type="text/javascript">
const slow=pHead;//慢指针走一步
const fast=pHead;//
while(slow!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(slow==fast) break;
}
if(slow==null || fast.next==null) return null;//表示链表没有环
const p1=pHead;//一个指向链表的起点
const p2=slow;//一个指向快慢指针相遇点
while(p1!=p2){
p1=p1.next;
p2=p2.next;
}
return p1;//p1和p2走了一样的距离并相遇时便是入口点
</script>
3 如果存在环,求环上的节点数
记录下相遇时的节点,并且存入临时变量temp中,然后慢指针继续往下走slow=slow.next,一直到slow=temp,此时经过的步数就是环的长度。
<script type="text/javascript">
const slow=pHead;//慢指针走一步
const fast=pHead;//
const step=1;
while(slow!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
const temp=slow;//找到了相遇点,并且把它记录下来
slow=slow.next;
break;
}
}
while(slow!=temp){//slow一直走到temp的位置,slow从相遇点开始走的步数就是环的长度
slow=slow.next;
step++;
}
return step;
</script>
4 如果存在环,求出链表的长度
解决思路:链表的长度=起点到入口的距离+环的长度
5 求出一个环上距离任意一个节点最远的点
问题:构建乘积数组
将数组B分开,分成左下三角和右上三角;左下三角c[i]=c[i-1]*A[i-1].右上三角 D[i]=D[i+1]*A[i+1];
问题24:删除有序链表中重复的节点,重复节点都不保留,返回头指针。
用当前cur指针和pre前驱指针,当两者相邻的时候说明cur还没有找到重复元素,不相邻的时候说明pre和cur之间有了部分相同的元素。
function ListNode(x){
this.val = x;
this.next = null;
}
function deleteDuplication(pHead)
{
// write code here
if(pHead==null){
return null;
}
var node=new ListNode(-1);
node.next=pHead;
var pre=node;
var cur=node.next;
while(cur!=null){
while(cur.next!=null && cur.val==cur.next.val){//一定要注意cur.next!=null
cur=cur.next;
}
if(pre.next!=cur){//cur找到重复元素往后移动了,才不和Pre挨着了
pre.next=cur.next;
cur=cur.next;
}else{//当还没遇到重复元素时,既pre 和cur还挨着的时候
pre=cur;//只有先让pre移动到cur,cur再移动,才能保证pre永远比cur慢一步
cur=cur.next;
}
}
return node.next;
}
问题25:二叉树的下一个结点。给定一课二叉树和其中的一个结点,如何找出中序遍历顺序的下一个结点?树中的结点除了有两个分别指向左右子结点的指针以外,还有一个指向父结点的指针。
该题的解题思路就是:
分两种情况:1.该节点有右子树,则右子树的最最左结点是所要找的节点
2.该节点没有右子树,则一直往上找,直到找到一个结点是其父结点的左子结点,则该父结点就是所要找的节点
/*function TreeLinkNode(x){
this.val = x;
this.left = null;
this.right = null;
this.next = null;
}*/
function GetNext(pNode)
{
// write code here
//分三种情况:
//1.当该节点有右子树,则右子树的最左子节点是所要找的节点
//2.当该节点没有右子树,则找当前节点是其父节点的左子结点的节点
if(pNode==null){
return null;
}
if(pNode.right!=null){//情况1
var pNode=pNode.right;
while(pNode.left!=null){
pNode=pNode.left;
}
return pNode;
}
while(pNode.next!=null){//情况2
if(pNode.next.left==pNode){
return pNode.next;
}
pNode=pNode.next;
}
}
问题26:实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
function isSymmetrical2(root1,root2){
if(root1 == null && root2 == null){
return true;
}
if(root1 == null || root2 == null){
return false;
}
if(root1.val != root2.val){
return false;
}
return isSymmetrical2(root1.left,root2.right) && isSymmetrical2(root1.right,root2.left);
}
function isSymmetrical(pRoot)
{
// write code here
return isSymmetrical2(pRoot,pRoot);
}
问题27:按之字形状从上到下打印二叉树。第一行按从左到右的顺序打印,第二行按从右到左的顺序打印,接下来的依次交换
解题思路:使用两个栈来操作,stack1用来暂存奇数行的节点,stack2用来暂存偶数行的节点。当奇数行的时候,将奇数行元素加入结果数组的同时,还把下一行的元素按照先左子节点后右子节点的顺序加入到stack2中;当偶数行的时候,将偶数行元素加入结果数组的同时,还把下一行的元素按照先右子节点后左子节点的顺序加入到stack1中;
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function Print(pRoot)
{
// write code here
var lists=new Array();//存放结果
if(pRoot==null){
return lists;
}
var stack1=[];//存放奇数行
var stack2=[];//存放偶数行
stack1.push(pRoot);
var i=1;//层数
while(stack1.length!=0 || stack2.length!=0){
var list=new Array();
if((i&1)==1){//当是奇数行的时候
while(stack1.length!=0){
var temp=stack1[stack1.length-1];//用一数组记录下,一直是从后往前push的
list.push(temp.val);
stack1.pop();
if(temp.left!=null){
//把下一行(偶数行)的数保存在stack2中,因为是从右往左,所以在栈中先添加左子节点
stack2.push(temp.left);
}
if(temp.right!=null){
stack2.push(temp.right);
}
}
}else{//当是偶数行的时候
while(stack2.length!=0){
var temp=stack2[stack2.length-1];
list.push(temp.val);
stack2.pop();
//因为奇数行是从左往右打印,所以在栈中先添加右子节点再添加左子节点
if(temp.right!=null){
stack1.push(temp.right);
}
if(temp.left!=null){
stack1.push(temp.left);
}
}
}
i++;
lists.push(list);
}
return lists;
}
问题28:从上到下把二叉树打印成多行。
解题思路: 在层序遍历+队列的基础上,判断每层的节点数;先把每层的节点放进数组中,然后再把这个数组放在结果数组中。
function Print(pRoot)
{
// write code here
if(pRoot==null){
return [];
}
var queue=[];
var res=[];
queue.push(pRoot);
while(queue.length!=0){
var len=queue.length;//一定要在这个地方标记一下queue的长度,以免queue添加的时候篡改
var tempArr=[];//用来临时保存一行的元素
for(var i=0;i<len;i++){
var node = queue.shift();//会自动的减掉queue的长度
tempArr.push(node.val);//将一行放在一个临时数组中
if(node.left!=null){
queue.push(node.left);
}
if(node.right!=null){
queue.push(node.right);
}
}
res.push(tempArr);
}
return res;
}
问题29 序列化一棵二叉树
解题思路:先将一个棵树深度遍历(前序遍历)到一个数组,其中将'a’作为树的叶子节点;然后再前序遍历反序列化
function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
}
let arr=[]
function Serialize(pRoot)
{
// write code here
if(pRoot!=null){//深度优先遍历
arr.push(pRoot.val);
Serialize(pRoot.left);
Serialize(pRoot.right);
}else{
arr.push('a');//把a当做叶子节点
}
}
function Deserialize()
{
// write code here
var root=null;
if(arr.length<0){
return null
};//接上一种情况,当arr里没有元素的时候
let node=arr.shift();
if(typeof node == 'number'){
root=new TreeNode(node);//将摘下来的node传给一个树节点
root.left=Deserialize();
root.right=Deserialize();
}
return root;
}
问题30 给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
解题思路:中序遍历+计数。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function KthNode(pRoot, k)
{
// write code here
if(pRoot==null || k==0){
return null;
}
function KthNodeCore(pRoot){//对这个函数进行递归
let target = null;//目标值
if(pRoot.left != null){ //中序遍历先遍历左子树
target = KthNodeCore(pRoot.left, k);
}
if(target == null){//当左子树遍历不到的时候,遍历根节点
if(k == 1){//k等于1说明找到了
target = pRoot;
}
k--;
}
if(target == null && pRoot.right != null){//当左子树和根都没有找到的话,遍历右子树
target = KthNodeCore(pRoot.right, k);
}
return target;
}
return KthNodeCore(pRoot);
}
问题:数组中重复的数字
由于数组里的所有数字都在0到n-1,也就是值也在数组的下标范围里。
我们可以这样做,通俗来讲,就是把数组中的每个值放到对应的下标的位置上。(数归其标)
- 把当前序列当成是一个下标和下标对应值是相同的数组;
- 遍历数组,判断当前位的值和下标是否相等: 2.1. 若相等,则遍历下一位; 2.2. 若不等,则将当前位置i上的元素和a[i]位置上的元素比较:若它们相等,则成功,即找到了重复的值!若不等,则将它们两交换。换完之后a[i]位置上的值和它的下标是对应的,但i位置上的元素和下标并不一定对应;重复2.2的操作,直到当前位置i的值也为i,将i向后移一位,再重复2.
function duplicate(numbers, duplication)
{
// write code here
//这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
//函数返回True/False
for(var i=0;i<numbers.length;i++){
while(i!=numbers[i]){//每次都是把元素放在它对应的位置
if(numbers[i]==numbers[numbers[i]]){
duplication[0]=numbers[i];
return true;
}
var temp=numbers[i];
numbers[i]=numbers[temp];
numbers[temp]=temp;
}
}
return false;
}