算法与数据结构简析

86 阅读5分钟

伪代码和流程图

逻辑

三段论逻辑

三段论逻辑就是从前两种结论为真的命题中推论出第三个命题,这个过程就叫做三段论逻辑。
譬如:

命题一:JS有7种数据类型:string、number、bool、symbol、underfined、null、object。
命题二:JS的函数不属于前六种类型。
推  论:JS函数属于object类型。
命题一:JS中的所欲函数都是由Function构造的。
命题二:Function、Object、Array是函数。
推  论:Function、Object、Array是由Function构造的。

如何用代码表示逻辑

例子:

输出两个数中较大的数

  • 如果第1个数字大于第二个数字,就输出第一个数字
  • 否则就输出第二个数字

代码:
这段用伪代码,方便理解,同时伪代码不用兼顾语法的使用细节,更突出如何去想。

a=[1,100]
if(a.get(0)>a.get(1)){
    print a.get(0)
}else{
    print a.get(1)
}

如果要输出N个数字中最大的数

  • 首先找到第1和第2个数字中较大的那个数组,存入Max
  • 再将Max与第三个数字的大小做对比,将较大的那个数字存入Max
  • 一直到N-1与N作比较,将较大的数字存入Max
  • Max就是得到最大的数
a=[1,3,5,6,8,22,67,111]
Max=a[0]
for(i from 1 to a.length()-1){
    if a.get(i)>Max then  Max=a.get(i)
print Max
}

顺序执行语句

由语句一向语句二按顺序执行

image.png

条件判断语句

通过判断条件,如果返回为真,则执行语句一,如果判断为假,则执行语句二

image.png

循环语句

进入一个循环语句,设置及一个初始的 i 值与判断条件,判断条件是否满足,如果返回为真,则执行语句一,然后 i=i+1 ,当不满足判断条件是,则执行语句二,跳出循环

image.png

流程图和伪代码的好处:1.锻炼你的大脑 2.整理你的思路

流程图的运用

选出两个数较大的数

image.png

选出多个数中最大的数

image.png

数据结构

数据与数据之间的关系与结构

如何表示两个数据

如果顺序有意义,则

[x,y]表示第一个是x,第二个是y
[y,x]表示第一个是y,第二个是x
比如坐标就是这样的数据
需要提供first和last操作

如果顺序无意义,则
(x,y)和(y,x)一样
比如血压值(120,80)和(80,120)没区别
不需要提供first和last操作

如何表示N个数据

如果顺序有意义,则

数组表示[a1,a2...aN]
要提供索引操作get(i)
要提供add/indexOf/delete操作

如果顺序无意义,则

集合表示{a1,a2...aN}
要提供add/delete/has操作

如何表示N对N数据

比如学号
哈希表表示
hash{10001="小红",10002=>"小兰"}
哈希表与JavaScript中的对象并不相同,只是形式相似,JavaScript中对象的key只能是字符串,而哈希表中的key可以是字符串,数字,甚至是对象。且对象具有原型,哈希表没有

min的实现

找出两个数字中较小的那个一个(minOf2)

let minOf2 = (numebrs) => {
  if (numbers[0] < number[1]) {
    return numbers[0];
  } else {
    return numbers[1];
  }
};

//优化代码

let minOf2 = (numbers) => (numebrs[0] < numbers[1] ? numbers[0] : numbers[1]);

//再优化代码

let minOf2 = ([a, b]) => (a < b ? a : b-- - 析构赋值);

JavaScript内置了Math.min

  • Math.min(1,2) // 1
  • Math.min.call(null,1,2)
  • Math,min.apply(null,[1,2])

关于Math:Math看起来像object一样是构造函数,实际上Math只是一个普通函数

找出多个数字中较小的那个一个(minOf2)

三个数字中找到最小的那一个

let minOf3 = ([a, b, c]) => {
  return minOf2(a, minOf2([b, c]));
};

以此类推,如果在四个数字中找到最小的那个则可以写成:

let minOf4 = ([a, b, c, d]) => {
  return minOf2(a, minOf3([b, c, d]));
};

N个数字中找到最小的那一个

let min = (numbers) => {
  if (numbers.lenght > 2) {
    return min([numbers[0], min(numbers.slice(1))]);
  } else {
    return Math.min.apply(null, numbers);
  }
};

递归

特点:函数不停的调用自己,每次调用的参数略有不同

排序算法

长度为2的数组排序

let sort2 = ([a, b]) => {
  if (a < b) {
    return [a, b];
  } else {
    return [b, a];
  }
};

//优化代码

let sort2 = ([a, b]) => (a < b ? [a, b] : [b, a]);

长度为3的数组排序

let minIndex = (numbers) => {
  numbers.indexOf(min(numbers));
};

let sort3 = (numbers) => {
  let index = minIndex(numbers);
  let min = numbers[index];
  numbers.splice(index, 1);
  return [min].concat(sort2(numbers));
};

以此类推,如果四个数字排序则可以写成:

let sort4 = (numbers) => {
  let index = minIndex(numbers);
  let min = numbers[index];
  numbers.splice(index, 1);
  return [min].concat(sort3(numbers));
};

任意长度的数组排序

let sort = (numbers) => {
  if (numbers.length > 2) {
    let index = minIndex(numbers);
    let min = numbers[index];
    numbers.splice(index, 1);
    return [min].concat(sort(numbers));
  } else {
    return numbers[0] < numbers[1] ? numbers : numbers.reverse();
  }
};

minIndex重写

let minIndex = (numbers) => {
  let index = 0;
  for (let i = 1; i < numbers.length; i++) {
    if (numbers[i] < numbers[index]) {
      index = i;
    }
  }
  return index;
};

用循环的思路重写sort

let swap =(array,i,j)=>{
    let temp = array[i]
    array[i] = array [j]
    array[j] = temp
}
let minIndex=(numbers)=>{
    let index = 0
    for(let i=1;i<numbers.length;i++){
        if(numbers[i]<numbers[index]){
               index=i
        }
    }
    return index
}
let sort =(numbers)=>{
    for(let i = 0 ;i<numbers.length-1;i++){
        let index =minIndex(numbers.slice(i))+i
      if(index!=i){swap(numbers,index,i)}  
    }
  return numbers
}

快速排序

let quickSort = (arr) => {
  if (arr.length <= 1) {
    return arr;
  }
  let pivotIndex = Math.floor(arr.length / 2);
  let pivot = arr.splice(pivotIndex, 1)[0];
  let left = [];
  let right = [];
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  return quickSort(left).concat([pivot], quickSort(right));
};

归并排序

let mergeSort = (arr) => {
  if (arr.length === 1) {
    return arr;
  }
  let left = arr.slice(0, Math.floor(arr.length / 2));
  let right = arr.slice(Math.floor(arr.length / 2));
  return merge(mergeSort(left), mergeSort(right));
};

let merge = (a, b) => {
  if (a.length === 0) {
    return b;
  }
  if (b.length === 0) {
    return a;
  }
  return a[0] < b[0]? 
    a[0].concat(merge(a.slice(1), b)): b[0].concat(merge(a, b.slice(1)));
};

计数排序

let countSort = (arr) => {
  let hashTable = {},
    max = 0,
    result = [];
  for (let i = 0; i < arr.length; i++) {
    if (!(arr[i] in hashTable)) {
      hashTable[arr[i]] = 1;
    } else {
      hashTable[arr[i]] += 1;
    }
  }
  if (arr[i] > max) {
    max = arr[i];
  }
  for (let j = 0; j <= max; j++) {
    if (j in hashTable) {
      for (let i = 0; i < hashTable[j]; i++) {
        result.push(j);
      }
    }
  }
  return result;
};

数据结构

队列 Queue

先进先出的数组

实现一个餐厅叫号的网页

  • 点击【取号】生成一个号码
  • 点击按钮【叫号】显示请xxx号就餐

栈 Stack

后进先出的数组

  • JavaScript函数的调用栈call Stack就是一个栈
  • 假设f1调用了f2,f2又调用了f3
  • 那么f3结束后应该回到f2,f2结束后应该回到f1
  • 压栈 push 弹栈pop

image.png

链表

let array = [1,2,3]
array.proto === Array.prototype
Array.prototype.proto === Object.prototype
从这个角度看对象就是链表

const createList = (value) => {
  return createNode(value);
};

const appendList = (list, value) => {
  const node = createNode(value);
  let x = list;
  while (x.next) {
    x = x.next;
  }
  x.next = node;
  return node;
};

const createNode = (value) => {
  return {
    data: value,
    next: null,
  };
};

const removeFromList = (list, node) => {
  let x = list;
  let p = node;
  while (x !== p && x !== null) {
    p = x;
    x = x.next;
  }
  if (x === null) {
    return false;
  } else if (x === p) {
    p = x.next;
    return p;
  } else {
    x.next = p.next;
    return list;
  }
};
const travelList = (list, fn) => {
    let x = list;
    while (x !== null) {
      fn(x);
      x = x.next;
    }
  };

哈希表 kay-value pairs

场景

假设哈希表hash里有一万对key-value
比如 name:'Zhoujia _ 22',age:18....
如何使得读取hash['xxx']速度最快

解决办法

不做任何优化,hash['xxx']需要遍历所有key,O(n)
对key排序,使用二分法查找,O(log2 n)
用字符串对应的ASCⅡ数字做索引O(1)
对索引做除法取余数O(1)
冲突了怎么办,冲突了就顺延

实际使用

中国的省市区,可以看成一棵树
公司的层级结构,可以看成一棵树
网页中的节点,可以看成一棵树

const createTree = (value) => {
  return {
    data: value,
    children: null,
    parent: null,
  };
};

const addChild = (node, value) => {
  const newNode = {
    data: value,
    children: null,
    parent: node,
  };
  node.children = node.children || [];
  node.children.push(newNode);
  return newNode;
};

const travel = (tree, fn) => {
  fn(tree);
  if (!tree.children) {
    return tree;
  }
  for (let i = 0; i < tree.children.length; i++) {
    travel(tree.children[i], fn);
  }
};

const find = (tree, node) => {
  if (tree === node) {
    return tree;
  } else if (tree.children) {
    for (let i = 0; i < tree.children.length; i++) {
      const result = find(tree.children[i], node);
      if (result) {
        return result;
      }
    }
    return undefined;
  } else {
    return undefined;
  }
};

const removeNode = (tree, node) => {
  const siblings = node.parent.children;
  let index = 0;
  for (let i = 0; i < siblings.length; i++) {
    if ((siblings[i] = node)) {
      index = i;
    }
  }
  siblings.splice(index, 1);
};