数据结构与算法

382 阅读1分钟

什么是复杂度

  • 程序执行时需要的计算量和内存空间(和代码是否简洁无关)
  • 复杂度是数量级,不是具体的数字。
  • 一般针对一个具体的算法,而非一个完整的系统

时间复杂度 - 程序执行时需要的计算量(CPU)

  • O(1)一次就够(数量级)
  • O(n)和传输的数据量一样(数据量)
  • O(n^2)数据量的平方(数据量)
  • O(logn)数量级的对数
 //o(1)
function fn(obj) {
  return obj.a + obj.b;
}

// o(n)
function fn2(arr) {
 for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
 }
}

 //O(n^2)
 function fn3(arr) {
   for (let i = 0; i < arr.length; i++) {
     for (let j = 0; j < arr.length; i++) {
       console.log(arr[j]);
      }
    }
 }
 
 // o(logn)
function fn4(arr) {
  for (let i = 0; i < arr.length; i++) {
    //二分法算法
   }
}

空间复杂度--程序执行时需要的内存空间

  • O(1)有限的,可数的空间
  • O(n)和输入的数据量相同的空间

把一个数组旋转K步

思路

  1. 把末尾的元素挨个pop,然后unshift到数组前面 / 时间复杂度o(n^2) ,空间复杂度o(1)
  2. 把数组拆分,最后concat拼接到一起 / 时间复杂度o(1),空间复杂度o(n) 【优先】
    function rotateArr(arr, k) {
       let len = arr.length;
        //判断是否旋转,数组是否有值
        if (!k || len === 0) return arr;
        //获取旋转的绝对步数
        let step = Math.abs(k % len);

        let arr1 = arr.slice(0, length - step);
        let arr2 = arr.slice(-step);
        let resArr = arr2.concat(arr1);
        return resArr;
     }

判断一个字符串是否括号匹配

思路

  1. 遇到左括号 {([就压栈
  2. 遇到右括号})]匹配栈顶,匹配就出栈
 function matchString(str) {
   let len = str.length;
   if (len === 0) return true;
   let arrSymbol = [];
        let leftSymbol = "{[(";
   let rightSymbol = ")]}";

    //验证括号
    function midefyValue(cur, top) {
      if (cur === "{" && top === "}") return true;
      if (cur === "[" && top === "]") return true;
      if (cur === "(" && top === ")") return true;
      return false;
    }
    for (let i = 0; i < len; i++) {
      s = str[i];
      //左侧数据入栈
     if (leftSymbol.includes(s)) {
       arrSymbol.push(s);
     } else if (rightSymbol.includes(s)) {
        //右侧数据出栈
       let topValue = arrSymbol[arrSymbol.length - 1];
       if (midefyValue(topValue, s)) {
         arrSymbol.pop();
       } else {
         return false;
       }
     }
   }
return arrSymbol.length === 0 ;

数组中三个数的最大乘积

思路

  1. 先把数组的顺序按照从大到小排列
  2. 考虑负数的情况,两个负数相乘成正数
function maxNum(arr){
    let sum=1,sum2=1;
    arr.sort((a,b)=>b-a)
    for(let i in arr){
        if(i<3){
            sum*=arr[i]
        }else{
        break;
        }
    }
    let len=arr.length
    if(len>3){
    sum2=arr[len-1]*arr[len-2]*arr[0]
    sum=sum>sum2?sum:sum2
    }
    return sum
}

手写归并排序

思路

  1. 把数据按照左右区分
  2. 对数组依次排序
 function sortArr(arr) {
        let len = arr.length;
        if (len <= 1) return arr;
        let mid = Math.floor(len / 2);
        let left = sortArr(arr.slice(0, mid));
        let right = sortArr(arr.slice(mid, len));
        return merge(left, right);

        function merge(left, right) {
          let [l, r] = [0, 0];
          let result = [];
          while (l < left.length && r < right.length) {
            if (left[l] < right[r]) {
              result.push(left[l]);
              l++;
            } else {
              result.push(right[r]);
              r++;
            }
          }

          result = result.concat(left.slice(l, left.length));
          result = result.concat(right.slice(r, right.length));
          return result;
        }
      }

链表

  • 链表是一种物流结构(非逻辑结构),类似于数组
  • 数组需要一段连续的内存空间,而链表是零散的
  • 链表节点的数据结构{value,next?,prev?}
const n1={
value:100,
next:n2
}

const n2={
value:200,
next:n3
}

const n3={
value:300
}

链表VS 数组

  • 都是有序结构
  • 链表:查询慢O(n),新增和删除快O(1)
  • 数组:查询快O(1),新增和删除慢O(n)

数组和链表,哪个实现队列更快?

  • 数组是连续存储,push很快,shift很慢
  • 链表是非连续存储,add和delete都很快(但查找很慢)
  • 结论:链表实现队列更快

定义一个JS函数,反转单向链表

思路

  1. 反转,即节点next指向前一个节点
  2. 但这很容易造成nextNode的丢失
  3. 需要三个指针prevNode,curNode,nextNode
   function reverseLinked(node) {
       prevNode = null;
       curNode = null;
       nextNode = node;

       //循环Node信息
       while (nextNode) {
         //判断是否第一个,为防止递归删除第一个next
         if (!prevNode && curNode) {
           delete curNode.next;
         }

         //反转指针
         if (curNode && prevNode) {
           curNode.next = prevNode;
         }

         // 整体向后移动指针
         prevNode = curNode;
         curNode = nextNode;
         nextNode = nextNode?.next;
       }
       curNode.next = prevNode;

       return curNode;
     }

数组转为一个链表形式

   function arrToLinked(arr) {
        let len = arr.length;
        if (len === 0) throw new Error("arr is empty");
        let res = null;

        for (let i = arr.length - 1; i >= 0; i--) {
          res = {
            value: arr[i],
            next: res,
          };
        }
        return res;
      }

用链表实现一个队列

class myQueue {
        head = null;
        tail = null;
        len = 0;
        add(value) {
          let node = {
            value: value,
            next: null,
          };
          // 判断是否存在head
          if (this.head == null) {
            this.head = node;
          }

          if (this.tail) {
            this.tail.next = node;
          }

          this.tail = node;
          this.len++;
        }
        delete() {
          if (this.head == null) return null;
          if (this.len <= 0) return null;
          let value = this.head;
          this.head = this.head.next;
          this.len--;
          return value;
        }
        get length() {
          return this.len;
        }
      }

二叉树

  • 是一棵树
  • 每个节点,最多只能有两个子节点
  • 树节点的数据结构{value,left?,right?}
  • 特点:left<=root;right>=root

求二叉搜索树的第K小值

 let arrLink = [];
      //二叉树中序遍历
      function linkToArr(node) {
        if (node == null) return;
        linkToArr(node?.left);
        arrLink.push(node?.value);
        linkToArr(node?.right);
      }

      function searchKValue(node, k) {
        //因为是搜索二叉树,先获取二叉树转换后的数组信息
        linkToArr(node);
        return arrLink[k - 1] || null;
      }

      const bst = {
        value: 5,
        left: {
          value: 3,
          left: {
            value: 2,
            left: null,
            right: null,
          },
          right: {
            value: 4,
            left: null,
            right: null,
          },
        },
        right: {
          value: 7,
          left: {
            value: 6,
            left: null,
            right: null,
          },
          right: {
            value: 8,
            left: null,
            right: null,
          },
        },
      };

      let binary = searchKValue(bst, 3);
      console.log("二叉树", binary); //二叉树  4

堆栈模型

  • JS代码执行时
  • 值类型变量,存储在栈
  • 引用类型变量,存储在堆

  • 完全二叉树
  • 最大堆:父节点>=子节点
  • 最小堆:父节点<=子节点