继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

【九月打卡】第44天 数据结构和算法(总结)

康遇
关注TA
已关注
手记 76
粉丝 3
获赞 9

数据结构

常见的数据结构有:栈、队列、链表、集合、字典、树、图、堆等等

  • 栈是一种后进先出的数据结构,可以通过数组Array的push和pop方法来模拟栈这种数据结构。

  • 栈的使用场景:十进制转二进制、判断字符串的括号是否有效、函数调用栈、二叉树的前序遍历等等

class Stack {
  constructor() {
    this.list = [];
  }
  push(item) {
    return this.list.push(item);
  }
  pop() {
    return this.list.pop();
  }
  peek() {
    return this.list[this.list.length - 1];
  }
}

队列

  • 队列是一种先进先出的数据结构,可以通过数组Array的push和shift方法来模拟栈这种数据结构。

  • 队列的使用场景:食堂打饭、火车站买票、异步任务队列、最近的请求次数等等

class Queue {
  constructor() {
    this.list = [];
  }
  push(item) {
    return this.list.push(item);
  }
  shift() {
    return this.list.shift();
  }
  peek() {
    return this.list[0];
  }
}

链表

  • 链表是多个元素组成的列表。

  • 和数组不同的是,链表的存储不是连续的,用next指针来连接起来。
    js中的链表使用Object来实现。

  • 链表的基本操作:遍历链表、插入链表、删除链表

class ListNode {
  constructor(val, next) {
    this.val = val;
    this.next = next;
  }
}

集合

  • 集合是无序且唯一的;ES中使用Set来表示集合;
  • 集合的Set的使用:new, has,add,delete,size
const set = new Set()   // Set(0) {size: 0}
set.add('a')            // Set(1) {1}
set.size                // 1
set.has('a')            // true
set.delete('a') 

字典

  • 和集合类似,字典也是存储唯一值的数据结构,不同的是字典是以键值对的形式来存储;表示一种映射关系。

  • ES6中使用Map来表示字典。

const map = new Map()   // Map(0) {size: 0}
map.set('a', 1)         // Map(1) {'a' => 1}
map.size                // 1
map.has('a')            // true
map.set('a', 100)       // Map(1) {'a' => 100}
map.get('a')            // 100
map.delete('a')         // true
map.clear()             // undefined

  • 树是一种分层数据的抽象模型

  • 前端中常见的树有:DOM树,级联菜单,树形控件等等

  • JS中没有树,前端可以用Object和Array来构建树。

  • 树的常用操作:深度优先遍历和广度优先遍历

  • 二叉树的常用操作:前序遍历、中序遍历、后序遍历

{
	value: 'a',
	lable: '第一个',
	childen: [
		{
			value: 'b',
			lable: '第二个',
		},
		{
			value: 'c',
			lable: '第三个',
		},
	]

}

  • 图是网络结构的抽象模型,是一组由边连接的节点

  • 图可以表示任何二元关系,比如道路,航班

  • js中用Object 和 Array来表示图

  • 图的表示法: 邻接矩阵、邻接表、关联矩阵…

  • 图的常用操作:深度优先遍历和广度优先遍历

  • 完全二叉树是指每层节点全部填满,最后一层如果不是满的,则只缺少右边的若干节点。

  • 堆是一种特殊的完全二叉树

  • 所有节点都大于等于(最大堆)或者小于等于(最小堆)它的子节点

  • 堆能快速高效的找出最大值,最小值;时间复杂度为O(1)

排序和搜索

常见的排序算法有:冒泡排序、选择排序、插入排序、归并排序、快速排序
常见的搜索算法有:顺序搜索、二分搜索

冒泡排序

  • 比较相邻元素,如果第一个比第二个大,则交换他们
  • 一轮下来保证最后一个是最大的
  • 执行n-1轮,完成排序
Array.prototype.bubbleSort = function () {
  for (let i = 0; i < this.length - 1; i++) {
    for (let j = 0; j < this.length - 1 - i; j++) {
      if (this[j] > this[j + 1]) {
        const temp = this[j];
        this[j] = this[j + 1];
        this[j + 1] = temp;
      }
    }
  }
}

时间复杂度:O(n^2)
空间复杂度:O(1)

选择排序

  • 选择数组中的最小值,并将其放到第一位
  • 接着寻找第二小的值,放到第二位
  • 依次执行n-1轮,选择排序完成
Array.prototype.selectSort = function () {
  for (let i = 0; i < this.length - 1; i++) {
    let minIndex = i;
    for (let j = i; j < this.length; j++) {
      if (this[j] < this[minIndex]) {
        minIndex = j
      }
    }
    if (i !== minIndex) {
      const temp = this[i];
      this[i] = this[minIndex];
      this[minIndex] = temp;
    }
  }
}

arr.selectSort()

时间复杂度:O(n^2)
空间复杂度:O(1)

插入排序

  • 从第二个数开始往前比
  • 如果有比它大的数就往后移
  • 依次类推,进行到最后一个数
Array.prototype.insertSort = function () {
  for (let i = 1; i < this.length; i++) {
    const temp = this[i];
    let j = i;
    while (j > 0) {
      if (this[j] < this[j - 1]) {
        this[j] = this[j - 1];
        j--;
      } else {
        break
      }
    }
    this[j] = temp;
  }
}

const arr1 = [6, 8, 5, 9, 3, 2, 1]
arr1.selectSort()
console.log(arr1)

时间复杂度:O(n^2)
空间复杂度:O(1)

归并排序

  • 分:把数组劈成两半,再递归对子数组进行“分”的操作,直至分成一个个单独的数
  • 合:把两个数合并为有序数组,再对有序数组进行合并,直至全部子数组合并为一个完整数组
    (1)新建一个空数组res,用于存放最终的数组
    (2)比较两个有序数组的头部,较小者出队并推入res中
    (3)如果两个数组还有值,就重复第二步
Array.prototype.mergeSort = function () {
  const rec = (arr) => {
    if (arr.length === 1) return arr;
    const mid = Math.floor(arr.length / 2);
    const left = arr.slice(0, mid);
    const right = arr.slice(mid)
    const orderLeft = rec(left)
    const orderRight = rec(right)
    const res = []
    while (orderLeft.length || orderRight.length) {
      if (orderLeft.length && orderRight.length) {
        res.push(orderLeft[0] < orderRight[0] ? orderLeft.shift() : orderRight.shift())
      } else if (orderLeft.length) {
        res.push(orderLeft.shift())
      } else if (orderRight.length) {
        res.push(orderRight.shift())
      }
    }
    return res;
  }
  rec(this)
}
const arr2 = [6, 8, 5, 9, 3, 2, 1]
const res = arr2.mergeSort()
console.log(res)

时间复杂度:O(nlogn)
空间复杂度:O(n)

快速排序

  • 分区:以数组中某个元素为基准,找出所有比他小的放前边,找出所有比他大的放后面;
  • 递归:递归对基准前后的子数组进行分区
  • 递归结束后返回排序后的数组
Array.prototype.quickSort = function () {
  const rec = (arr) => {
    if (arr.length <= 1) {
      return arr
    }
    const left = [];
    const right = [];
    const mid = arr[0];
    for (let i = 1; i < arr.length; i++) {
      if (arr[i] < mid) {
        left.push(arr[i])
      } else {
        right.push(arr[i])
      }
    }
    return [...rec(left), mid, ...rec(right)];
  }

  const res = rec(this)
  res.forEach((n, i) => {
    this[i] = n
  })
}
const arr = [6, 8, 5, 9, 3, 2, 1];
arr.quickSort();
console.log(arr);

时间复杂度:O(nlogn)
空间复杂度:O(n)

顺序搜索

  • 数组从头开始遍历,找出相应的元素下标,找不到返回-1

  • 时间复杂度为O(n),搜索效率低

function sequential(arr, target) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === target) {
      return i;
    }
  }
  return -1;
}
sequential([1, 2, 3, 4], 3)

二分搜索

二分搜索只适用于有序数组;如果是乱序数组,需要先排序再进行二分搜索

  • 从数组的中间元素开始,如果中间元素等于目标元素,搜索结束
  • 如果目标值大于或者小于中间元素,则在大于或者小于中间元素的那一半数组中搜索
function binarySearch(arr, target) {
  let start = 0;
  let end = arr.length - 1;
  while (start <= end) {
    let mid = Math.floor((start + end) / 2);
    if (target > arr[mid]) {
      start = mid + 1;
    } else if (target < arr[mid]) {
      end = mid - 1;
    } else {
      return mid
    }
  }
  return -1;
}

binarySearch([1, 2, 3, 4], 5)

时间复杂度:O(logn)
空间复杂度:O(1)

算法思想

常见的算法思想有:分而治之、动态规划、贪心算法、回溯算法等等

分而治之

  • 它将一个问题分为多个和原问题相似的小问题,递归或迭代解决小问题,再将结果合并来解决原来的问题。

  • 使用场景:归并排序、快速排序、猜数字大小、翻转二叉树、对称二叉树等等

动态规划

  • 动态规划将一个问题分解成互相重叠的子问题,通过反复解决子问题来解决原来的问题。

  • 动态规划是分解成互相重叠的子问题;分而治之是分解为相互独立的子问题

  • 使用场景:爬楼梯、打家劫舍等等

贪心算法

  • 期望通过每个阶段的局部最优选择,达到全局最优

  • 但是结果不一定最优

  • 使用场景:分饼干、买卖股票等等

回溯算法

  • 回溯算法是一种渐进式寻找并构建问题解决方式的策略

  • 回溯算法从一个可能的动作开始,如果不行,就回溯选择另外一个,直到将问题解决。

  • 使用场景:数组全排列、找数组子集等等

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP