1.列举常见的数据结构
衍生问题1:栈和队列的区别?
栈是先进后出,队列是先进先出。
栈只允许在表尾一端进行插入和删除,队列只允许在表尾一端进行插入,在表头一端进行删除。
衍生问题2:栈和堆的区别?
栈区:由编辑器自动分配释放,存放函数的参数值,局部变量的值等(基本类型值)。
堆区:由程序员分配释放,若程序员不释放,程序结束时可能有OS回收(引用类型值)。
栈(数据结构):一种先进后出的数据结构。
堆(数据结构):堆可以被看成是一棵树,如:堆排序。
衍生问题3:数组和链表的区别?它们的优缺点?
区别:
1.数组的所有的元素在内存中都是连续存储的,而链表则是分散在内存中的,是通过指针连接起来的。
2.数组的插入数据和删除数据效率低,但是访问速率高O(1)。链表任意位置插入元素和删除元素的速度快,时间复杂度是o(1),但是随机访问效率低,时间复杂度是o(N)。
链表的优点:
1 任意位置插入元素和删除元素的速度快,时间复杂度是o(1)
2 内存利用率高,不会浪费内存
3 链表的空间大小不固定,可以动态拓展。
链表的缺点:
随机访问效率低,时间复杂度是O(1)
数组的优点:
随机访问性强,查找速度快,时间复杂度是O(1)
数组的缺点:
1 从头部删除、从头部插入的效率低,时间复杂度是o(n),因为需要相应的向前搬移和向后搬移。
2 空间利用率不高
3 内存空间要求高,必须要有足够的连续的内存空间。
4 数组的空间大小是固定的,不能进行动态扩展。
2.将一个数组乱序?
1.使用sort
function shuffle(arr) {
return arr.sort(() => Math.random() > 0.5 )
}
console.log(shuffle([1,2,3,4,5,6]));
2.使用for循环
function shuffle(arr) {
for (let i = 1; i < arr.length; i++) {
const random = Math.floor(Math.random() * (i + 1));
[arr[i], arr[random]] = [arr[random], arr[i]];
}
return arr;
}
console.log(shuffle([1,2,3,4,5,6]));
3.写一下数组、链表反转
数组反转:
function reverse(arr){
for(var i = 0;i<arr.length/2;i++){
[arr[i],arr[arr.length - 1 - i]] = [arr[arr.length - 1 - i],arr[i]]
}
return arr
}
var a = [1,2,3,4,5,6]
console.log(reverse(a)); //[6,5,4,3,2,1]
链表反转:
(function (){ //这里定义一个自执行函数
//构建链表 这里要注意,链在最后的对象要最开始声明,不然取到next的值就是undefined,这里变量提升,都先赋值为undefined
var obj3 = {
name:'obj3',
next:null
}
var obj2 = {
name:'obj2',
next:obj3
}
var obj1 = {
name:'obj1',
next:obj2
}
function printLst(node){ //这段是用来输出链表的
var p = node;
while(p){
console.log(p.name)
p = p.next;
}
}
function reverse(nodeLst){
var pNode = nodeLst;
var pPre = null; //翻转之后 第一个节点的next值 为 null
var pNext;
while(pNode){
pNext = pNode.next; //获取到当前节点的下一个节点
pNode.next = pPre; //当前节点的前一个指向上一个节点
pPre = pNode; //上一个节点赋值为当前节点
pNode = pNext; //当前节点赋值为下一个节点
}
return pPre;
}
printLst(reverse(obj1));
})();
4.将两个有序链表合并成一个?
在生成一个新的链表时,提供一个常数作为参数,避免了在直接输入l1或者l2的头结点时还需要逻辑判断的复杂情况。直接在最后返回时,从该链表的第二个开始返回。
var mergeTwoLists = function(l1, l2) {
var l3 = new ListNode(-1);
var c3 = l3;
while(l1 !== null && l2 !== null) {
if(l1.val <= l2.val) {
c3.next = l1;
l1 = l1.next;
} else {
c3.next = l2;
l2 = l2.next;
}
c3 = c3.next;
}
//循环完某一链表后,将另一链表剩下的部分直接加入到l3
c3.next = (l1 == null) ? l2 : l1;
return l3.next;
};
function ListNode(val) {
this.val = val;
this.next = null;
}
5.判断链表是否有环?
function hasCycle( head ) {
// write code here
while(head){
if(head.visited) return true
head.visited = 1
head = head.next
}
return false
}
6.如何用两个栈实现一个队列?
思路:stack1用来添加数据,而stack2用来提取数据。
var stack1 = []
var stack2 = []
function push(node)
{
// write code here
stack1.push(node)
}
function pop()
{
// write code here
if(stack1.length === 0 && stack2.length === 0) return;
if(stack2.length === 0){
for(var i=0,len=stack1.length;i<len;i++){
stack2.push(stack1.pop())
}
}
return stack2.pop()
}
module.exports = {
push : push,
pop : pop
};
7.输入两个链表,找出它们的第一个公共结点
思路:如果两个链表存在公共节点,那么公共节点出现在两个链表的尾部。如果我们从两个链表的尾部开始往前比较,那么最后一个相同的节点就是我们要找的节点。但是这两个链表是单向的,要实现尾节点最先比较,我们可以借助两个辅助栈。分别将两个链表的节点放入两个栈中,这样栈顶就是两个链表的尾节点,比较两个栈顶节点是否相同,如果相同,将栈顶弹出比较下一个栈顶,直到找到最后一个相同的栈顶。时间复杂度O(m + n)。
function FindFirstCommonNode(pHead1, pHead2)
{
// write code here
if(!pHead1 || !pHead2) return null
let stack1 = []
let stack2 = []
while(pHead1){
stack1.push(pHead1)
pHead1 = pHead1.next
}
while(pHead2){
stack2.push(pHead2)
pHead2 = pHead2.next
}
let node = null
while(stack1.length && stack2.length){
let pH1 = stack1.pop()
let pH2 = stack2.pop()
if(pH1.val == pH2.val){
node = pH1
}else{
break
}
}
return node
}
8.给定一个链表,请判断该链表是否为回文结构。
function isPail( head ) {
let stack = []
let cur = head
while(cur){
stack.push(cur.val)
cur = cur.next
}
while(head){
if(head.val !== stack.pop()) return false
head = head.next
}
return true
}
9.实现链表尾部插入新节点?
function append(element){
let node = new Node(element);
let current;
if(this.head == null){ //链表为空时
this.head = node;
}else{
current = this.head;
while(current.next){
current = current.next; //通过循环得到最后一个元素
}
current.next = node; //将随后一个元素的指针指向要插入的node
}
length++;
}
function Node(ele){//定义链表中的一个元素
this.ele = ele;//值
this.next = null;//用来指向下一个节点的指针
}
向指定位置插入元素?
function insert(position, element){
if(position > length || position < 0 ){
return false;
}
let node = new Node(element);
let current = this.head;
let index = 0;
let previous;
if(position == 0){ //若在第一个位置添加
node.next = current;
this.head = node;
}else{
while(index++ < position){
previous = current;
current = current.next;
}//找出要插入的位置previous 以及current 分别代表所插入元素位置的一前一后
node.next = current;
previous.next = node;
}
length++;
return true;
}
10.给定一个链表,删除链表的倒数第n个节点并返回链表的头指针
思路:可以通过快指针先走n步 慢指针先指向head,导致 快指针和慢指针相差n个结点,然后快指针移到末尾 这个时候慢指针就是倒数第n个结点了。
function removeNthFromEnd( head , n ) {
let fast = head,slow = head
for(let i=0;i<n;i++){
fast = fast.next
}
if(!fast) return head.next //如果是倒数第n个的话就直接返回第二个节点
while(fast.next){ //快指针移到末尾
fast = fast.next
slow = slow.next
}
slow.next = slow.next.next //慢指针的下一个就是要被删除的结点
return head
}
8.说说树的定义?
树是n个节点的有限集。n=0时称为空树,在任意一颗非空树中:1.有且仅有一个特定的称为根的节点。2.当n>1时,其余节点可分为m个互不相交的有限集。
衍生问题1:说说二叉树的定义?
二叉树是n个节点的有限集和,由一个根节点和两颗互不相交的称为根节点的左子树和右子树的二叉树组成。
衍生问题2:操作给定的二叉树,将其变换为源二叉树的镜像?
function Mirror(root)
{
if(!root) return null
let temp = root.left
root.left = Mirror(root.right)
root.right = Mirror(temp)
return root
}
衍生问题3:给定一棵二叉树,判断琪是否是自身的镜像(即:是否对称)
function isSymmetric( root ) {
return isSymmetricTree(root,root)
}
function isSymmetricTree(left,right){
if(!left && !right) return true
if(!left || !right) return false
if(left.value != right.value) return false
return isSymmetricTree(left.left,right.right) && isSymmetricTree(left.right,right.left)
}
衍生问题4:给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
给定如下二叉树,以及目标和 sum = 22,
function hasPathsum(root,sum){
if(!root) return false
if(!root.left && !root.right) return root.value == sum
let target = sum - root.value
return hasPathsum(root.left,target) || hasPathsum(root.right,target)
}
衍生问题5:如何判断一棵树是否为平衡二叉树?
平衡二叉树:一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
function IsBalanced_Solution(pRoot)
{
let status = true
function d(root){
if(!root) return 0
let leftDepth = d(root.left)
let rightDepth = d(root.right)
if(!status) return -1
if(Math.abs(leftDepth - rightDepth) > 1){
status = false
}
return Math.max(leftDepth,rightDepth) + 1
}
d(pRoot)
return status
}