前端常见数据结构和算法总结

315 阅读8分钟

作为一名前端,还是需要学习一下相关的数据结构和算法知识,虽然平时不常用到,但是大厂面试必不可少。接下来呢,就来总结一下前端的常见数据结构和算法。

初级算法

线性查找-时间复杂度O(n)--相当于算法界中的HelloWorld

var arr = new Array(1,2,10,9,5);

//线性搜索
//A为数组,x为要搜索的值
function linearSearch(A,x){
	for(var i=0;i<A.length;i++){
		if(A[i]==x){
			return i;
		}
	}
	return false;
}
document.write(linearSearch(arr,10)+"</br>");

二分查找

(又称折半查找) - 适用于已排好序的线性结构 - 时间复杂度O(logN)

//二分搜索
//A为已按"升序排列"的数组,x为要查询的元素
//返回目标元素的下标
var arr1 = new Array(1,2,10,19,25);
function binarySearch(A, x){
	var low = 0, high = A.length - 1;
	while(low<=high){
		var mid=Math.floor((low+high)/2);//下取整
		if(A[mid]==x){
			return mid;
		}else if(A[mid]>x){
			high=mid-1;
		}else{
			low=mid+1;
		}
	}
	return false;
}
document.write(binarySearch(arr1,19)+"</br>");

冒泡排序

时间复杂度O(n^2)

var arr2 = new Array(1,2,10,9,5);
//第一种
function bubbleSort(A){
	var i,j,flag,temp;
	for(i=A.length-1;i>=1;i--){
		flag=0;
		for(j=1;j<=i;j++){
			if(A[j-1]>A[j]){
				temp=A[j];
				A[j]=A[j-1];
				A[j-1]=temp;
				flag=1;
			}		
		}
		if(flag==0)
				return;
	}
}
/*
第二种
function bubbleSort(A) {
  for (var i = 0; i < A.length; i++) {
    var sorted = true;
  //注意:内循环是倒着来的
    for (var j = A.length - 1; j > i; j--) {
      if (A[j] < A[j - 1]) {
        var temp=A[j];
        A[j]=A[j-1];
        A[j-1]=temp;
        sorted = false;
      }
    }
    if (sorted) {
      return;
    }
  }
}
第三种(推荐:容易写,不容易出错)
function bubbleSort(arr){
	var flag=0;
	do{
		flag=1;
		for(var i=0 ; i<arr.length;i++){
			if(arr[i]>arr[i+1]){
				var temp=arr[i];
				arr[i]=arr[i+1];
				arr[i+1]=temp;
				flag=0;
            }
        }
	}
	while(flag==0)
}
*/
bubbleSort(arr2);

选择排序

时间复杂度O(n^2)


//思路:找到最小值的下标记下来,再交换
function selectionSort(A){
	for(var i=0;i<A.length;i++){
		var k=i;
		//这个是算法的关键,它从无序序列中挑出一个最小的元素
		for(j=i+1;j<A.length;j++){
			if(A[k]>A[j])
				k=j;
		}
		//下面3句完成最小元素与无序序列第一个元素的交换
		var temp=A[i];
		A[i]=A[k];
		A[k]=temp;
	}
}
var arr3=[14,35,25,12,64,33];
selectionSort(arr3);
document.write("</br>");
for(var i=0;i<arr3.length;i++)
document.write(arr3[i]+",");
document.write("</br>");

插入排序

时间复杂度O(n^2)

假定当前元素之前的元素已经排好序,先把自己的位置空出来,然后前面比自己大的元素依次向后移,直到空出一个"坑",然后把目标元素插入"坑"中每趟将一个待排序的元素作为关键字,按照其关键字值的大小插入到已经排好的部分序列的适当位置,直到插入完成

function insertSort(A){
	for(var i=1;i<A.length;i++){//数组从下标0开始存储,第一个元素有序,所以从第二个开始处理
		var temp=A[i];//将待插入元素暂存于temp中
		var j=i-1;
		//下面这个循环完成了从待排元素之前的元素开始扫描,如果大于待排元素,则后移一位
		while(j>=1&&temp<A[j]){
			A[j+1]=A[j];
			j--;
		}
		A[j+1]=temp;
	}
}
arr4=[45,342,45634,664,656,34342,46,56,544];
insertSort(arr4);
for(var i=0;i<arr4.length;i++)
document.write(arr4[i]+",");
document.write("</br>");

字符串反转

时间复杂度O(logN)

字符串反转(比如:ABC -> CBA) 可以直接用str.split("").reverse().join("")

var str='ABC';
function reverse(s){
	var a=s.split('');//把‘ABC’分成子串数组['A','B','C']
	var i=0,j=a.length-1;
	while(i<j){
		var temp=a[i];
		a[i]=a[j];
		a[j]=temp;
		i++;
		j--;
	}
	return a.join('');//把字符串重新组合
}
var k=reverse(str);
document.write(k);
document.write("</br>");

设计一个基于对象的单链表

(双链表类似)

需要设计两个类,Node 类用来表示节点, LinkedList 类提供插入节点、删除节点、显示列表元素的方法,以及其他一些辅助方法。

//Node类:
function Node(element){
	this.data=element;//当前节点的数据 
	this.next=null;//下一个节点数据
	//this.previous = null;(双链表)
}
//LinkedList类
function LList(){
	this.head=new Node("head");//头节点 
}
//链表的操作
LList.prototype={
	//查找某一节点
	find:function(item){
		var currNode=this.head;
		while(currNode.data!=item){
			currNode=currNode.next;
		}
		return currNode;
	},
	//向某一元素后面加入新节点
	insert:function(newElement,item){
		var newNode=new Node(newElement);
		var current=this.find(item);
		newNode.next=current.next;
		current.next=newNode;
		//newNode.previous = current;(双链表)
	},
	//查找某一节点的前一个节点(前驱)
	findPrevious:function(item){
		var currNode=this.head;
		//从头结点开始找,一直到当前结点的next==item是返回当前结点
		while (!(currNode.next == null) &&(currNode.next.element != item)) {
			currNode=currNode.next;
		}
		return currNode;
	},
	//删除某一个节点
	remove:function(item){
		var prevNode=this.findPrevious(item);
		if (!(prevNode.next == null)){
			prevNode.next = prevNode.next.next; 
			/*(双链表)
			currNode.previous.next = currNode.next;  
    		currNode.next.previous = currNode.previous;  
    		currNode.next = null;  
    		currNode.previous = null;  */
		}    
	},
	//修改某一节点的数据
	edit:function(item,newItem){
		var curNode=this.find(item);
		curNode.data=newItem;
	},
	//在控制台打印出所有节点(为了方便预览)
	print:function(){
		var currNode=this.head;
		while (!(currNode.next == null)){
			console.log(currNode.next.data);    
      		currNode = currNode.next;
		}
	}
}
//测试链表
//新建链表
var names=new LList();
names.insert("likek", "head");//往头节点后插入节点likek  
names.insert("zhangsan", "likek");//往likek后插入节点zhangsan  
names.insert("lisi", "zhangsan");//往zhangsan后插入节点lisi  
names.insert("wangwu", "lisi");//往lisi后插入节点wangwu  
names.print(); 
console.log("****************************");
//移除结点
names.remove("zhangsan");//删除zhangsan节点
names.print();
console.log("****************************");
//更改结点
names.edit("lisi","wangnima");//将lisi节点改为wangnima
names.print();
console.log("****************************");

用数组实现的队列

利用数组中的push和shift方法可以使队列的实现显得非常简单

var name1=[];
name1.push("wuxinpei");//推入元素
name1.push("zhangjionghuan");
name1.push("suguojing");
console.log(name1);//输出数组
console.log(name1.join());//输出字符串
name1.shift();//出队
console.log(name1.join());
console.log("****************************");

从定义一个Queue构造函数开始实现队列

function Queue(){
	this.dataStore=[];
}
Queue.prototype={
	enqueue:function (element) {  
   this.dataStore.push(element);//入队,加入新成员  
 },  
  dequeue:function(){  
     return this.dataStore.shift();//删除并返回队首元素  
  },  
  front:function(){  
     return this.dataStore[0];//返回队首元素  
  },  
  back:function(){  
     return this.dataStore[this.dataStore.length-1];//返回队尾元素  
  },  
  toString:function(){  
     return this.dataStore.join();//返回队列中所有元素  
  },  
  isempty:function(){  
     return !this.dataStore.length;//判断队列是否为空  
  }  
}
//测试队列
var ui=new Queue();  
ui.enqueue("same");  
ui.enqueue("哈哈");  
ui.enqueue(26);  
console.log(ui.toString());//"likek,哈哈,18"
console.log("****************************");  
/*ui.dequeue();//"likek"  
ui.toString();//"likek,哈哈"  
ui.isempty();//false  
ui.dequeue();ui.dequeue();  
ui.isempty();//true 
*/

JS使用队列对数组排列,基数排序算法

/*
* 使用队列对数组排列,基数排序
*对于0~99的数字,基数排序将数组集扫描两次。
* 第一次按个位上的数字进行排序,
* 第二次按十位上的数字进行排序
* */
function Queue(){
  this.dataStore = [];//存放队列的数组,初始化为空
  this.enqueue = enqueue;//向队列尾部添加一个元素
  this.dequeue = dequeue;//删除队首的元素
  this.theFront = theFront;//读取队首的元素
  this.back = back;//对取队尾的元素
  this.toStrings = toStrings;//显示队列内的所有元素
  this.empty = empty;//判断队列是否为空
}
function enqueue(element){
  this.dataStore.push(element);
}
function dequeue(){
  return this.dataStore.shift();
}
function theFront(){
  return this.dataStore[0];
}
function back(){
  return this.dataStore[this.dataStore.length-1];
}
function toStrings(){
  return this.dataStore;
}
function empty(){
  if(this.dataStore.length == 0){
    return true;
  }else{
    return false;
  }
}
/*基数排序
* nums :需要排序的数组
* queues :数组,里面元素是队列
* n :队列的格式,这里为10个
* digit :传入1,则先按个位上的数字排序;传入10,则按十位上的数字排序
 * */
function distribute(nums,queues,n,digit){
  for(var i = 0;i < n;i++){
    if(digit == 1){
      queues[nums[i]%10].enqueue(nums[i]);
    }else if(digit == 10){
      queues[Math.floor(nums[i]/10)].enqueue(nums[i]);
    }else{
    }
  }
}
function collect(queues,nums){
  var i = 0;
  for(var j = 0;j < 10;j++){
    while(!queues[j].empty()){
      nums[i++] = queues[j].dequeue();
    }
  }
}
/*测试程序*/
var queues = [];
for(var i = 0;i < 10;i++){
  queues[i] = new Queue();
}
var nums = [];
for(var i = 0;i < 10;i++){
  nums[i] = Math.floor(Math.random()*101);
}
console.log("开始的nums: "+nums);//24,72,90,84,49,69,8,30,50,0 (都是随机数,仅供参考)
distribute(nums,queues,10,1);
collect(queues,nums);
distribute(nums,queues,10,10);
collect(queues,nums);
console.log("排序后的nums: "+nums);//0,8,24,30,49,50,69,72,84,90 (都是随机数,仅供参考)

栈的实现

从定义一个stack构造函数开始

function Stack() {  
 this.dataStore = [];//保存栈内元素  
 this.top = 0;  
}  
//对栈的各种操作 
Stack.prototype={  
   push:function push(element) {  
          this.dataStore[this.top++] = element;//添加一个元素并将top+1  
        },  
   peak:function peak() {  
          return this.dataStore[this.top-1];//返回栈顶元素  
        },  
   pop:function pop() {  
          return this.dataStore[--this.top];//返回栈顶元素并将top-1  
       },  
   clear:function clear() {  
           this.top = 0;//将top归0  
         },  
   length:function length() {  
            return this.top;//返回栈内的元素个数  
          }  
}  
//测试:
var lk=new Stack();  
lk.push("likeke");  
lk.push("zhangsan");  
lk.push("wangwu");   
lk.length();//3 
console.log(lk.peak()); 
lk.pop();//"wangwu"  
lk.clear();  
lk.length();//0  

二叉树的实现

具体代码如下

//Node节点
function Node(data,left,right){
	this.data = data;
	this.left = left;
	this.right = right;
	this.show = show;
}

//展示当前节点数据
function show(){
	return this.data;
}

//树结构
function BST(){
	this.root = null;
	this.insert = insert;
	this.inOrder = inOrder;
	this.getMin = getMin;
	this.getMax = getMax;
	this.find = find;
	this.remove = remove;
}

//插入节点操作
function insert(data){
	let n = new Node(data,null,null);
	if(this.root == null){
		this.root = n;
	}else{
		let current = this.root;
		let parent;
		while(current){
			parent = current;
			if(data < current.data){
				current = current.left;
				if(current == null){
					parent.left = n;
					break;
				}
			}else{
				current = current.right;
				if(current == null){
					parent.right = n
					break
				}
			}
		}
	}
}

//中序遍历
function inOrder(node){
	if(!(node == null)){
		inOrder(node.left);
		console.log(node.show());
		inOrder(node.right)
	}
}

//先序遍历
function preOrder(node){
	if(!(node == null)){
		console.log(node.show());
		preOrder(node.left);
		preOrder(node.right);
	}
}

//后序遍历
function postOrder(node){
	if(!(node == null)){
		postOrder(node.left);
		postOrder(node.right);
		console.log(node.show())
	}
}
//层序遍历(广度优先搜索):队列
function levelOrder(node){
  const printArr = []
  if (!root) return printArr
  const list = []
  list.push({ node: root, level: 0 })
  while (list.length > 0) {
    const { node, level } = list.shift()
    if (!printArr[level]) {
      printArr[level] = []
    }
    printArr[level].push(node.val)
    node.left && list.push({ node: node.left, level: level + 1 })
    node.right && list.push({ node: node.right, level: level + 1 })
  }
  return printArr
}
//二叉树查找最小值
function getMin(){
	let current = this.root;
	while(!(current.left == null)){
		current = current.left
	}
	return current.data
}

//二叉树查找最大值
function getMax(){
	let current = this.root;
	while(!(current.right == null)){
		current = current.right
	}
	return current.data
}

//查找给定值
function find(data){
	let current = this.root;
	while(current != null){
		if(current.data == data){
			return current;
		}else if(data < current.data){
			current = current.left
		}else {
			current = current.right
		}
	}
	return null;
}

//移除节点
function remove(data){
	root = removeNode(this.root,data)
}

//获取右子树最小值
function getSmallest(node){
	if(node.left == null){
		return node;
	}else{
		return getSmallest(node.left)
	}
}

//移除节点操作
function removeNode(node,data){
	if(node == null){
		return null;
	}
	if(data == node.data){
		//没有子节点的节点
		if(node.left == null && node.right == null){
			return null;
		}
		//没有左子节点的节点
		if(node.left == null){
			return node.right;
		}
		//没有右子节点的节点
		if(node.right == null){
			return node.left;
		}
		// 有2个子节点的节点
		let tempNode = getSmallest(node.right);
		node.data = tempNode.data
		node.right = removeNode(node.right,tempNode.data);
		return node;
	}else if(data < node.data){
		node.left = removeNode(node.left,data);
		return node;
	}else{
		node.right = removeNode(node.right,data);
		return node;
	}
}

let nums = new BST();
nums.insert(23);
nums.insert(45);
nums.insert(16);
nums.insert(37);
nums.insert(3);
nums.insert(99);
nums.insert(22);

let min = nums.getMin();
console.log(min);

let max = nums.getMax();
console.log(max);

let value = nums.find("45");
console.log(value);

nums.remove(23);

图的实现

function Graph(v){
	this.vertices = v; //顶点的数量
	this.edges = 0
	this.adj = []
	for(let i=0;i<this.vertices;++i){
		this.adj[i] = []; //保存与顶点i相邻的顶点列表
	}
	this.addEdge = addEdge
	this.showGraph = showGraph
	this.dfs = dfs
	this.bfs = bfs
	this.marked = [] //保存未访问过的顶点
	for(let i=0;i<this.vertices;++i){
		this.marked[i] = false
	}
}

function addEdge(v,w){
	this.adj[v].push(w);
	this.adj[w].push(v);
	this.edges++;
}

function showGraph(){
	for(let i=0;i<this.vertices;++i){
		let str = '';
		str += i + ' -> ';
		for(let j= 0;j<this.vertices;++j){
			if(this.adj[i][j] != undefined){
				str += this.adj[i][j] + ' '
			}
		}
		console.log(str)
	}
}

//深度优先搜索
function dfs(v){
	this.marked[v] = true;
	if(this.adj[v] != undefined){
		console.log('Visited vertex: ' + v)
	}
	for(let w of this.adj[v]){
		if(!this.marked[w]){
			this.dfs(w)
		}
	}
}

//广度优先搜索
function bfs(s){
	let queue = []
	this.marked[s] = true
	queue.push(s) //添加到队尾
	while(queue.length > 0){
		let v = queue.shift(); //从队首移除
		if(v != undefined){
			console.log('Visited vertex: ' + v)
		}
		for(let w of this.adj[v]){
			if(!this.marked[w]){
				this.marked[w] = true;
				queue.push(w)
			}
		}
	}
}

let g = new Graph(5);
g.addEdge(0,1);
g.addEdge(0,2)
g.addEdge(1,3);
g.addEdge(2,4);
g.showGraph();
g.dfs(0)
g.bfs(0)

快速排序

交换排序和递归--平均复杂度O(nlog2n)

arr5=[49,38,65,97,76,13,27];
function quickSort(arr,l,r){//对从arr[l]到arr[r]的元素进行排序
	var temp,i=l,j=r;
	if(i<j){
		temp=arr[i];
		while(i!=j){
			while(i<j&&arr[j]>temp){//从右往左扫描到一个小于temp的元素
				j--;
			}
			if(i<j){
				arr[i]=arr[j];//把j的元素放在i的位置上(temp的左边)
				i++;//i右移一位
			}
			while(i<j&&arr[i]<temp){//从左往右扫描找到一个大于temp的元素
				i++;
			}
			if(i<j){
				arr[j]=arr[i];//把i的元素放在j的位置上(temp的右边)
				j--;
			}
		}
		arr[i]=temp;//将temp放在最终位置
		quickSort(arr,l,i-1);//递归地对temp左边的元素进行排序
		quickSort(arr,i+1,r);//递归地对temp右边的元素进行排序
	}
}
quickSort(arr5,0,6);
console.log(arr5);

两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

//给定 nums = [2, 7, 11, 15], target = 9

//因为 nums[0] + nums[1] = 2 + 7 = 9
//所以返回 [0, 1]
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let a = {}
    for(let i=0;i<nums.length;i++){
        let temp = target - nums[i]
        if(a[temp] != undefined) return [a[temp],i];
        a[nums[i]] = i
    }
    return []
};

三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。答案中不可以包含重复的三元组。

//例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

//满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
//方法一:
var threeSum = function(nums) {
    let res = []
      let length = nums.length;
      nums.sort((a, b) => a - b) // 先排个队,最左边是最弱(小)的,最右边是最强(大)的
      if (nums[0] <= 0 && nums[length - 1] >= 0) { // 优化1: 整个数组同符号,则无解
        for (let i = 0; i < length - 2;) {
          if (nums[i] > 0) break; // 优化2: 最左值为正数则一定无解
          let first = i + 1
          let last = length - 1
          do {
            if (first >= last || nums[i] * nums[last] > 0) break // 两人选相遇,或者三人同符号,则退出
            let result = nums[i] + nums[first] + nums[last]
            if (result === 0) { // 如果可以组队
              res.push([nums[i], nums[first], nums[last]])
            }
            if (result <= 0 ) { // 实力太弱,把菜鸟那边右移一位
              while (first < last && nums[first] === nums[++first]){} // 如果相等就跳过
            } else { // 实力太强,把大神那边右移一位
              while (first < last && nums[last] === nums[--last]) {}
            }
          } while (first < last)
          while (nums[i] === nums[++i]) {}
        }
      }
      return res
};

//方法二:
var threeSum = function(nums) {
    //排序
    nums = nums.sort((a,b) => a-b);
    let res = [],
        len = nums.length;

    for(let i=0;i<len -2;i++){
        let j = i+1, k = len -1;

        //固定元素大于0退出
        if(nums[i] > 0){
            break
        }
        //固定元素遇到重复跳过
        if(nums[i] == nums[i-1]){
            continue;
        }

        //左指针小于右指针,右指针不要小于0
        while(j < k && nums[k] >= 0){
            //三数之和
            let sum = nums[i] + nums[j] + nums[k];
            
            //等于0
            //加入结果数组
            //让左右指针继续移动
            if(sum === 0){
                res.push([nums[i],nums[j],nums[k]])
                j++;
                k--;
                //左指针遇重复跳过
                while(j < k && nums[j] == nums[j-1]){
                    j++
                }
                //右指针遇重复跳过
                while(j < k && nums[k] == nums[k+1]){
                    k--
                }
            }else if(sum > 0){ //大于0 ,右指针左移
                k--
                //右指针遇重复跳过
                while(j < k && nums[k] == nums[k+1]){
                    k--
                }
            }else{
                j++
                 //左指针遇重复跳过
                while(j < k && nums[j] == nums[j-1]){
                    j++
                }
            }
        }
    }
    return res
}

两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function(l1, l2) {
    let p1 = l1
    let p2 = l2
    let carry = 0
    const dummy = new ListNode()
    let pointer = dummy
    while (p1 || p2 || carry) {
        const num1 = p1 ? p1.val : 0
        const num2 = p2 ? p2.val : 0
        const sum = num1 + num2 + carry
        if (sum > 9) {
			pointer.next = new ListNode(sum % 10)
			carry = 1
        } else {
			pointer.next = new ListNode(sum)
			carry = 0
        }
        if (p1) p1 = p1.next
        if (p2) p2 = p2.next
        pointer = pointer.next
    }
    return dummy.next
};

合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var mergeTwoLists = function(l1, l2) {
     // 设置莫须有的头结点作为返回链表的头结点
    var prevHead = new ListNode(-1);
    // 利用js对象地址传递的方式动态更新范围链表的尾结点插入操作
    var prevNode = prevHead;
    // 临界值 
    while (l1 != null && l2 != null) {
        // 每次寻找并仅插入一个尾结点
        if(l1.val <= l2.val){
            // 插入尾结点
            prevNode.next = l1; 
            // 更新指针
            l1 = l1.next
        }else{
            //同上
            prevNode.next = l2;
            l2 = l2.next;
        }
        // js对象引用传递的特性 prevHead 已经更新成加了当前尾结点
        // prevNode 赋值为 返回链表的尾结点对象地址 && 并不修改原地址 循环往复下 只会更新 原链表对象地址的尾结点元素值
        // 当前 prevNode.next 即相当于 原链表的尾部结点地址 类似 原链表.next.next.....next
        prevNode = prevNode.next;
    }
    // 因子问题均有序 剩下不为空结点的链表可以直接追加到 合并好的部分结果链表中
    // 此时的prevNode即为合并好的部分的结果链表的尾结点
    prevNode.next = l1 ? l1 :l2;
    // 返回哨兵结点的next即为所求
    return prevHead.next;
};

最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let ans = nums[0];
    let sum = 0;
    for(let i=0;i<nums.length;i++){
        if(sum > 0){
            sum += nums[i]
        }else{
            sum = nums[i]
        }
        ans = Math.max(ans,sum)
    }
    return ans
};

背包问题

背包问题是算法研究中的一个经典问题。试想你是一个保险箱大盗,打开了一个装满奇珍异宝的保险箱,但是你必须将这些宝贝放入你的一个小背包中。保险箱中的物品规格和价值不同。你希望自己的背包装进的宝贝总价值最大。

如果在我们例子中的保险箱中有 5 件物品,它们的尺寸分别是 3、4、7、8、9,而它们的价值分别是 4、5、10、11、13,且背包的容积为 16,那么恰当的解决方案是选取第三件物品和第五件物品,他们的总尺寸是 16,总价值是 23。

初始数据

let capacity = 16;
let objectArr = [
    {value: 4, size: 3},
    {value: 5, size: 4},
    {value: 10, size: 7},
    {value: 11, size: 8},
    {value: 13, size: 9}
];

普通递归

function knapsack(capacity,objectArr,order){
    if(capacity == 0 || order == 0) return 0;
    if(objectArr[order].size > capacity){
        return knapsack(capacity,objectArr,order - 1)
    }
    return Math.max(objectArr[order].value + knapsack(capacity - objectArr[order].size, objectArr, order - 1),
                    knapsack(capacity, objectArr, order - 1));
}

console.log(knapsack(capacity, objectArr, objectArr.length-1)); // 23

动态规划

function knapsack2(capacity, objectArr) {
    var n = objectArr.length;
    var f = [];
    for (var w = 0; w <= capacity; w++) {
        for (var i = 0; i < n; i++) {
            if (w === 0) {
                f[w] = 0;
            } else if (objectArr[i].size <= w) {
                var size = objectArr[i].size,
                    value = objectArr[i].value
                f[w] = Math.max(f[w - size] + value, f[w] || 0);
            } else {
                f[w] = Math.max(f[w] || 0, f[w - 1]);
            }
        }
    }
    return f[capacity];
}

console.log(knapsack2(capacity, objectArr)); // 23

贪心算法 (前提需要尺寸与背包大小匹配,装满背包)

function knapsack3(capacity, objectArr) {
    // 首先按性价比排序, 高 -> 低
    objectArr.sort(function (a, b) {
        return parseFloat(b.value / b.size) - parseFloat(a.value / a.size);
    });
    // 记录物品个数
    var n = objectArr.length;
    // 记录已经选中尺寸,已选最大的最大价值
    var selected = 0,
        maxValue = 0;
    for (var i = 0; i < n && selected < capacity; i++) {
        var size = objectArr[i].size,
            value = objectArr[i].value;
        if (size <= capacity - selected) {
            maxValue += value;
            selected += size;
        } else {
            // 计算比例
            maxValue += value * ((capacity - selected) / size);
            selected  = capacity;
        }
    }
    return maxValue;
}

console.log(knapsack3(capacity, objectArr)); // 23

八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

function queen(arr,cur){
    let a = arr.concat();
    if(cur==a.length){
        console.log(a);
        return;
    }
    for(let i=0;i<a.length;i++){
        a[cur] = i;
        let flag = true;

        //与之前路径对比,看是否符合落子,相当于check
        for(let j=0;j<cur;j++){
            let ab = i-a[j];
            if(a[j] == i || (ab > 0 ? ab:-ab) == cur-j ){//是否正对 || 斜对角线
                flag = false;
                break;
            }
        }
        if(flag){
            queen(a,cur+1)
        }
    }
}

queen([1,1,1,1,1,1,1,1],0) 

实战:LeetCode:51. N皇后

更多