排序与树结构

递归与排序

选择排序(递归实现)

定义:从头至尾扫描序列,找出最小(大)的一个元素,和第一个(最后)元素交换,接着从剩下的元素中继续这种选择和交换方式,最终得到一个有序序列。

例子:假设有n个元素的数组A[0:n-1),要求按照元素的大小关系得到递增序列,也就是A中最小的元素在第0位,最大的元素在第n-1位。
不妨设书架上摆了n本无顺序的书,希望重新排列这n本书,让它们按照书作者姓的首个拼音按字母顺序进行排序。比如作者唐二(Tang)的书应该排在作者为赵三(Zhao)的书前面,因为字母表中T在Z的前面。
在这里插入图片描述
如图:首先,找到拼音字母顺序最后的一本书(设在位置i)。之后,将这本书 A【i】与最后一本书 A【n-1】交换位置,这样最后一个位置摆什么书就确定了。再重复该过程。
不妨设已经存在一个算法叫select_sort,它可以对数组A中的n个元素排序。也就是,select_sort(A[0:n-1]),以下等式成立:

select_sort(A[0:n -1])= select_sort(A[0:n -2])+ max(A[0:n - 1])、

其中,select_sort(A0:n-2])是原问题 select_sort(A[0:n-1)的子问题,max(A[0:n-1])则是求出序列 A[0:n -1] 中最大的元素

function select_sort(arr, n) {
  if (n === 0) {
    //边界条件
    return;
  }
  var max = n; //将当前最大元素索引设置为数组最后一个元素
  for (let index = 0; index < n; index++) {
    //遍历arr,找出前n-1个元素中最大元素
    if (arr[index] > arr[max]) {
      //更新max的下标
      max = index;
    }
  }
  //遍历完arr找出最大元素后与最后一个元素交换
  var change = arr[max];
  arr[max] = arr[n];
  arr[n] = change;

  //再重复该递归方程
  select_sort(arr, n - 1);
}
let array = [2, 8, 5, 3];
select_sort(array, 3);
console.log("排序后", array);

在这里插入图片描述

选择排序(循环实现)

双重循环实现功能

function select_sort(arr) {
  //从后往前遍历
  for (let index = arr.length - 1; index > 0; index--) {
    let max = index;
    //从arr第一个元素遍历到index前一个元素,并找出当中最大元素的下标
    for (let j = 0; j < index - 1; j++) {
      if (arr[j] > arr[max]) {
        max = j;
      }
    }
    //遍历完arr找出最大元素后与最后一个元素交换
    var change = arr[max];
    arr[max] = arr[index];
    arr[index] = change;
  }
}
let array = [2, 8, 5, 3];
select_sort(array);
console.log("排序后", array);

插入排序(递归实现)

将一组数据分成两组(有序组与无序组)。每次从无序组中取出一个元素,与有序组的元素进行比较,找到合适位置将其插入。每次插入一个元素,有序组增加,无序组减少。直到无序组元素个数为0。
在这里插入图片描述
设定最后一个元素为最小值,将其往前挪

function insert_sort(arr, n) {
  if (n === 0) {
    return;
  }
  let min = n;//设定当前元素最小值下标为数组最后一个
  insert_sort(arr, n - 1);//递归
  while (min > 0 && arr[min - 1] > arr[min]) {//当min!=0,并且min下标的前一个数比自身大
    //交换arr[min - 1] 与 arr[min]
    let change = arr[min];
    arr[min] = arr[min - 1];
    arr[min - 1] = change;
    //将min-1,继续遍历
    min--;
  }
}
let array = [2, 9, 8, 5, 3];
insert_sort(array, array.length - 1);
console.log("插入排序后", array);

插入排序(循环实现)

function insert_sort(arr) {
  let n = arr.length;
  if (n === 0) {
    return;
  }
  for (let i = n - 1; i > 0; i--) {
    for (let j = 0; j < i; j++) {
      if (arr[i] < arr[j]) {
        //交换arr[i] 与 arr[j]
        let change = arr[i];
        arr[i] = arr[j];
        arr[j] = change;
      }
    }
  }
}
let array = [2, 9, 8, 5, 3, 10, 11, 15, 19, 1];
insert_sort(array);
console.log("插入排序后", array);

合并排序(分治算法)

先分解(将数组分成两半,一直分到只有一个元素)再合并
在这里插入图片描述

let array = [2, 9, 8, 5, 11, 12, 14, 10, 7, 4, 23];
const middle = parseInt(array.length / 2);
const arrLeft = array.slice(0, middle);
const arrRight = array.slice(-middle);
//const arrRight = array.slice(middle, array.length);

将数组切割成两半,对左右两半进行合并处理,再重复该过程

let array = [2, 9, 8, 5, 11, 12, 14, 10, 7, 4, 23];
const middle = parseInt(array.length / 2);
const arrLeft = array.slice(0, middle);
const arrRight = array.slice(-middle);
function merge_sort(arr) {
  const n = arr.length;
  if (n <= 1) {
    //递归边界
    return arr;
  }
  const middle = parseInt(n / 2);
  const arrLeft = arr.splice(0, middle);
  const arrRight = arr.splice(-middle);
  const left = merge_sort(arrLeft);
  const right = merge_sort(arrRight);
  return this.merge(left, right);
}
function merge(left, right) {
  let res = [];
  let i = 0;
  let j = 0;
  while (i < left.length && j < right.length) {
    if (left[i] < right[j]) {
      res.push(left[i]);
      i++;
    } else {
      res.push(right[j]);
      j++;
    }
  }
  while (i < left.length) {
    res.push(left[i]);
    i++;
  }
  while (j < right.length) {
    res.push(right[j]);
    j++;
  }
  return res;
}
let array = [2, 9, 8, 5, 11, 12, 14, 10, 7, 4];
const result = merge_sort(array);
console.log("合并排序后", result);

二叉搜索树

节点N的左子树<节点N<节点N的右子树
飞机降落计划求解问题:
假如现在需要给一个很小的飞机场开发一个程序,该程序的主要功能就是管理飞机的降落计划。由于这个飞机场非常小,因此它只有一个跑道,也就是说某个时刻只能允许一架飞机降落。为了确保安全,前后两架飞机降落需要一定的安全时间间隔,设为k。
假定小机场的n次降落计划数据已经保存。如果某架飞机的飞行员向塔台发送一个信号“塔台,是否允许我在t时刻降落”。塔台便需要运行我们这个程序,如果回答是肯定的,那么将这架飞机的降落计划添加到原计划当中;否则,将拒绝该架飞机的降落请求。由于飞机运行需要非常高的时效,因此机场方面要求程序能在O(logn)这一时间内给出回应。
假设已经存在的降落计划 R=[41,46,49,56],其中的数字表示各架飞机降落时间。安全时间间隔设为k=3,且当前时刻为36。那么,时刻 44 这一降落时间点便不被允许因为与已有的在 46 这一时间点的降落计划冲突(|46-44|<k)。时刻53的降落请求则可以给予肯定的回复,而时刻 20 则不被允许,因为它的降落时间小于当前时间 36。

插入节点

class Node {
  //创建节点
  constructor(val) {
    this.root = this;
    this.val = val;
    this.left = null;
    this.right = null;
  }
}
class BinarySearchTree {
  constructor() {
    this.root = null; //初始化根节点
  }
  insert(val) {
    const newNode = new Node(val);
    const insertNode = (node, newNode) => {
      //判断t是否与node节点冲突
      if (newNode.val - node.val < 3) {
        return Error;
      }
      //不冲突的情况,往左子树走
      else if (newNode.val < node.val) {
        if (node.left === null) {
          //如果左子树为空,t插入成为左子树
          node.left = newNode;
        } else {
          //继续递归,判断左子树该节点与t的关系
          insertNode(node.left, newNode);
        }
      }
      //不冲突的情况,往右子树走
      else if (newNode.val > node.val) {
        if (node.right === null) {
          //如果右子树为空,t插入成为右子树
          node.right = newNode;
        } else {
          //继续递归,判断右子树该节点与t的关系
          insertNode(node.right, newNode);
        }
      }
    };
    if (!this.root) {
      this.root = newNode;
    } else {
      insertNode(this.root, newNode);
    }
  }
}
const tree = new BinarySearchTree();
tree.insert(41);
tree.insert(46);
tree.insert(49);
tree.insert(56);
tree.insert(44);
console.log(tree);

中序遍历所有节点(左根右)

inOrderTraverse() {
    let res = [];
    const inOrderSearch = (root) => {
      if (!root) {
        return;
      } else {
        inOrderSearch(root.left); // 递归遍历出左节点
        res.push(root.val); // 将值push到数组里
        inOrderSearch(root.right); // 递归遍历出右节点
      }
      return res;
    };
    return inOrderSearch(this.root);
  }

先序遍历所有节点(根左右)

preOrderTraverse() {
    let res = [];
    const preOrderSearch = (root) => {
      if (!root) {
        return;
      } else {
        res.push(root.val); // 将值push到数组里
        preOrderSearch(root.left); // 递归遍历出左节点
        preOrderSearch(root.right); // 递归遍历出右节点
      }
      return res;
    };
    return preOrderSearch(this.root);
  }

后序遍历所有节点(左右根)

postOrderTraverse() {
    let res = [];
    const postOrderSearch = (root) => {
      if (!root) {
        return;
      } else {
        postOrderSearch(root.left); // 递归遍历出左节点
        postOrderSearch(root.right); // 递归遍历出右节点
        res.push(root.val); // 将值push到数组里
      }
      return res;
    };
    return postOrderSearch(this.root);
  }

查找最小值

max() {
    let max = this.root.val;
    const saerchMax = (root) => {
      if (!root) {
        return;
      } else {
        if (root.val > max) {
          max = root.val;
        }
        saerchMax(root.left);
        saerchMax(root.right);
      }
      return max;
    };
    return saerchMax(this.root);
  }
 min(node) {
    const minNode = (node) => {
      return node ? (node.left ? minNode(node.left) : node) : null;
    };
    return minNode(node || this.root);
  }

查找最大值

 min() {
    let min = this.root.val;
    const saerchMin = (root) => {
      if (!root) {
        return;
      } else {
        if (root.val < min) {
          min = root.val;
        }
        saerchMin(root.left);
        saerchMin(root.right);
      }
      return min;
    };
    return saerchMin(this.root);
  }
  max(node) {
    const maxNode = (node) => {
      return node ? (node.right ? maxNode(node.right) : node) : null;
    };
    return maxNode(node || this.root);
  }

查找特定值

 search(data) {
    const searchNum = (root, data) => {
      if (!root) {
        return null;
      } else {
        if (root.val === data) {
          return root;
        }
        return searchNum(data < root.val ? root.left : root.right, data);
      }
    };
    return searchNum(this.root, data);
  }

从树中移除某个键(最复杂)

remove(data) {
    // 删除节点复杂之处在于每次删除节点时候二叉树要根据不同情况改变结构 同样也需要递归
    const removeNode = (node, data) => {
      if (node === null) return null;
      if (node.data === data) {
        if (node.left === null && node.right === null) return null;
        if (node.left === null) return node.right;
        if (node.right === null) return node.left;
        if (node.left !== null && node.right !== null) {
          let _node = this.min(node.right);
          node.data = _node.data;
          node.right = removeNode(node.right, 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;
      }
    };
    return removeNode(this.root, data);
  }

所有代码

class Node {
  //创建节点
  constructor(val) {
    this.root = this;
    this.val = val;
    this.left = null;
    this.right = null;
  }
}
class BinarySearchTree {
  constructor() {
    this.root = null; //初始化根节点
  }
  //从树中移除某个键
  remove(data) {
    // 删除节点复杂之处在于每次删除节点时候二叉树要根据不同情况改变结构 同样也需要递归
    const removeNode = (node, data) => {
      if (node === null) return null;
      if (node.data === data) {
        if (node.left === null && node.right === null) return null;
        if (node.left === null) return node.right;
        if (node.right === null) return node.left;
        if (node.left !== null && node.right !== null) {
          let _node = this.min(node.right);
          node.data = _node.data;
          node.right = removeNode(node.right, 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;
      }
    };
    return removeNode(this.root, data);
  }
  //查找特定值
  search(data) {
    const searchNum = (root, data) => {
      if (!root) {
        return null;
      } else {
        if (root.val === data) {
          return root;
        }
        return searchNum(data < root.val ? root.left : root.right, data);
      }
    };
    return searchNum(this.root, data);
  }
  //找出最小值
  min() {
    let min = this.root.val;
    const saerchMin = (root) => {
      if (!root) {
        return;
      } else {
        if (root.val < min) {
          min = root.val;
        }
        saerchMin(root.left);
        saerchMin(root.right);
      }
      return min;
    };
    return saerchMin(this.root);
  }
  //找出最大值
  max() {
    let max = this.root.val;
    const saerchMax = (root) => {
      if (!root) {
        return;
      } else {
        if (root.val > max) {
          max = root.val;
        }
        saerchMax(root.left);
        saerchMax(root.right);
      }
      return max;
    };
    return saerchMax(this.root);
  }
  // 后序遍历所有节点(左右根)
  postOrderTraverse() {
    let res = [];
    const postOrderSearch = (root) => {
      if (!root) {
        return;
      } else {
        postOrderSearch(root.left); // 递归遍历出左节点
        postOrderSearch(root.right); // 递归遍历出右节点
        res.push(root.val); // 将值push到数组里
      }
      return res;
    };
    return postOrderSearch(this.root);
  }
  // 先序遍历所有节点(根左右)
  preOrderTraverse() {
    let res = [];
    const preOrderSearch = (root) => {
      if (!root) {
        return;
      } else {
        res.push(root.val); // 将值push到数组里
        preOrderSearch(root.left); // 递归遍历出左节点
        preOrderSearch(root.right); // 递归遍历出右节点
      }
      return res;
    };
    return preOrderSearch(this.root);
  }
  // 中序遍历所有节点(左根右)
  inOrderTraverse() {
    let res = [];
    const inOrderSearch = (root) => {
      if (!root) {
        return;
      } else {
        inOrderSearch(root.left); // 递归遍历出左节点
        res.push(root.val); // 将值push到数组里
        inOrderSearch(root.right); // 递归遍历出右节点
      }
      return res;
    };
    return inOrderSearch(this.root);
  }
  //插入
  insert(val) {
    const newNode = new Node(val);
    const insertNode = (node, newNode) => {
      //判断t是否与node节点冲突
      //   if (newNode.val - node.val < 3) {
      //     console.log(newNode.val, "冲突");
      //     return Error;
      //   }
      //不冲突的情况,往左子树走
      if (newNode.val < node.val) {
        if (node.left === null) {
          //如果左子树为空,t插入成为左子树
          node.left = newNode;
        } else {
          //继续递归,判断左子树该节点与t的关系
          insertNode(node.left, newNode);
        }
      }
      //不冲突的情况,往右子树走
      else if (newNode.val > node.val) {
        if (node.right === null) {
          //如果右子树为空,t插入成为右子树
          node.right = newNode;
        } else {
          //继续递归,判断右子树该节点与t的关系
          insertNode(node.right, newNode);
        }
      }
    };
    if (!this.root) {
      this.root = newNode;
    } else {
      insertNode(this.root, newNode);
    }
  }
}
const tree = new BinarySearchTree();
tree.insert(11);
tree.insert(7);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.insert(20);
tree.insert(18);
tree.insert(25);
console.log(tree.inOrderTraverse());
console.log(tree.preOrderTraverse());
console.log(tree.postOrderTraverse());
console.log(tree.min());
console.log(tree.max());
console.log(tree.search(20));
console.log(tree.remove(7));

堆(二叉树与二叉搜索树不同)

完全二叉树

  • 每个节点最多只有两个子节点
  • 二叉树最下面的两层结点的度能够小于2,并且最下面一层的结点都集中在该层最左边的若干位置。

堆上任意结点的值均大于(小于)该结点所有子结点的值
简单而言,堆是一颗完全二叉树,它将序列中值最大(小)的元素置于树的根部。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值