数据结构算法-刷leetcode

解题思路

用什么数据结构

按照存储方式分数组和链表 (数组是连续空间存储,顺序存储只有data,随机访问,快速查找。链表是非连续空间,链式存储,需要指针+data)
按照结构分(图、散列表、队列、栈、树等)

在这里插入图片描述
stl里的容器
vector(动态数组,存储是连续的,内存分配根据需要会变化)
set
map 红黑树实现,有key+value 可以方便查找。
unordered-map hash表实现 大量无序数据可以用 可以存链表中重复元素 先遍历一遍 存入
stack 入栈出栈 二叉树的层序遍历 广度优先

queue 先进先出

用什么算法

暴力解法
循环遍历+递归
在这里插入图片描述

优化算法
1 、牺牲空间换取时间效率提高
2、二分法
3、数组用堆(完全二叉树)的特性
4、动态规划(f【n】和f【n-1】有关系的问题):
暴力解法->用for循环n*m次 ,(把f【n】【m】的问题变成f[i][j])把遍历问题变成f【n】和f【n-1】的关系
优化时:用空间换取时间 (增加note memory记事本)
5、 状态转移
6、深度优先广度优先(拓扑排序,寻路(走迷宫),搜索引擎,爬虫等,)
树的广度和深度决定用哪个)
广度优先( 二叉树的层序遍历 )
深度优先(二叉树前序遍历, 中序遍历, 后序遍历)有递归和非递归
7、二叉树遍历前序遍历 当前节点-左节点-右节点

递归

    public static void dfs(Node treeNode) { 
        if (treeNode == null) { 
            return; 
        } 
        // 遍历节点 
        process(treeNode) 
        // 遍历左节点 
        dfs(treeNode.left); 
        // 遍历右节点 
        dfs(treeNode.right); 
    } 

递归的表达性很好,也很容易理解,不过如果层级过深,很容易导致栈溢出。

所以我们重点看

非递归-栈实现-前序遍历 根-左-右


对于每个节点来说,先遍历当前节点,然后把右节点压栈,再压左节点(这样弹栈的时候会先拿到左节点遍历,符合深度优先遍历要求)。

弹栈,拿到栈顶的节点,如果节点不为空,重复步骤 1, 如果为空,结束遍历。

我们以以下二叉树为例来看下如何用栈来实现 DFS。

* 使用栈来实现 dfs 
 
public static void dfsWithStack(Node root) { 
    if (root == null) { 
        return; 
    } 
 
    Stack<Node> stack = new Stack<>(); 
    // 先把根节点压栈 
    stack.push(root); 
    while (!stack.isEmpty()) { 
        Node treeNode = stack.pop(); 
        // 遍历节点 
        process(treeNode) 
 
        // 先压右节点 
        if (treeNode.right != null) { 
            stack.push(treeNode.right); 
        } 
 
        // 再压左节点 
        if (treeNode.left != null) { 
            stack.push(treeNode.left); 
        } 
    } 
} 

中序遍历:左–>根–>右的形式,与先序遍历很相似,只是打印的位置不同。(相对来说是最简单的)

 public static void inOrderTraveralWithStack(Node node){
       Stack<Node> stack = new Stack<>();

       while (node != null || !stack.isEmpty()){
           while (node != null){
               stack.push(node);
               node = node.left;//将所有的左节点放完---while **左节点**
           }
           if(!stack.isEmpty()){
               node = stack.pop();
               System.out.print(node.date+" ");//输出**根节点**
               node = node.right;//每次pop弹出栈把当前节点(**根**)的右节点设为当前节点(下一轮对它(**右节点**)操作)
           }
       }
    }

非递归-队列实现-层序遍历

队列先进先出 刚好和层序这种顺序符合
每次根节点先进,然后出来根节点,同时把左右排进队列,然后按照左出列,然后把左的左右排入队列。按照队列顺序下来是右出列,然后右的左右。。

//    层序遍历
    public static void levelOrderTraver(Node node){
        Queue<Node> queue = new LinkedList<>();
//        插入根结点
        queue.offer(node);
        while (!queue.isEmpty()){
            Node pop = queue.poll();
            System.out.print(pop.date+" ");
            if(pop.left != null){
                queue.offer(pop.left);
            }
            if(pop.right != null){
                queue.offer(pop.right);
            }
        }
    }

后序遍历:左–>右–>根

,相比前两种遍历,其应该是最难得,从上面两种遍历可以看出,都是先从根结点出发,依次的找出相对的所有左孩子,然后会出栈到它相对的根结点,再次判断,因此在后序遍历中,访问完左孩子,接下来应该是访问右孩子,所以就应该引入一个变量来记录根结点其右孩子的情况(包括不存在和已经访问过得情况)。

public static void postOrderTraverTraveralWithStack(Node node){
       Stack<Node> stack = new Stack<>();
       Node pre = null;  //上次访问的节点
       while (node != null || !stack.isEmpty()){
           //将根结点以及所有左孩子入栈
           while (node != null){
               stack.push(node);
               node = node.left;
           }
           //走到这说明该节点的左孩子为空了,接下来只需要判断右孩子
           if(!stack.isEmpty()){
               //获取栈顶元素
               Node top = stack.peek();
               //如果栈顶元素的右孩子不存在或者已经被访问了,则输出栈顶元素
               if(top.right == null || top.right == pre){
                   node = stack.pop();
                   System.out.print(node.date+" ");
                   //为本次访问的节点赋值
                   pre = node;
                   //更新node,很重要否则会进入死循环,(为了访问该节点的根结点)
                   node = null;
               }//找到栈顶元素的右孩子,并且会将右孩子压入栈(while中),继续迭代
               else {
                   node = top.right;
               }

           }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值