二叉搜索树,Map,Set,LeetCode刷题

ops/2024/11/15 8:43:11/

二叉搜索树,Map,Set

  • 1. 二叉搜索树
  • 2. 二叉搜索树的模拟实现
    • 1.1 插入操作
    • 1.2 查找操作
    • 1.3 删除操作
  • 3. 二叉搜索树时间复杂度分析
  • 4. TreeMap和TreeSet
  • 5. Map
    • 5.1 Map的常用方法
    • 5.2 Map.Entry<K,V>
  • 6. Set
    • 6.1 Set的常用方法
  • LeetCode刷题
    • 1. 二叉搜索树与双向链表

1. 二叉搜索树

二叉搜索树是一棵特殊的树,它是“有序的”,因此又被叫做二叉排序树,它具有以下的性质:

  • 根节点左子树的任意元素都小于根节点的元素
  • 根节点右子树的任意元素都大于根节点的元素
  • 上述两条性质对于该树的任意子树都成立

2. 二叉搜索树的模拟实现

待实现代码:

public class BinarySearchTree {static class TreeNode {public int key;public TreeNode left;public TreeNode right;TreeNode(int key) {this.key = key;}}public TreeNode root;/*** 插入一个元素* @param key*/public boolean insert(int key) {return true;}//查找key是否存在public TreeNode search(int key) {return null;}//删除key的值public boolean remove(int key) {return false;}
}

1.1 插入操作

插入一个新节点,首先要遍历原树,找到一个合适的位置去插入,而插入的节点会成为新树的叶子节点

具体实现如下:

    public boolean insert(int key) {if(root == null) {root = new TreeNode(key);return true;}TreeNode node = new TreeNode(key);TreeNode cur = root;TreeNode curParent = null;//找插入新节点的合适位置while(cur != null) {curParent = cur;if(cur.key < key) {cur = cur.right;}else if(cur.key > key) {cur = cur.left;}else {return false;}}//进行插入新节点操作if(curParent.key < key) {curParent.right = node;}else {curParent.left = node;}return true;}

1.2 查找操作

    public TreeNode search(int key) {TreeNode cur = root;while(cur != null) {if(cur.key < key) {cur = cur.right;}else if(cur.key > key) {cur = cur.left;}else {return cur;}}return null;}

1.3 删除操作

二叉搜索树的删除操作是三个操作里最难的,难在需要考虑的情况较多,需要不断用测试用例去调试代码,可以用下面这个链接去验证代码是否还有遗漏的情况

题目链接:NC297 删除一个二叉搜索树中的节点

实现代码:

    public boolean remove(int key) {TreeNode cur = root;TreeNode curParent = root;//找到删除节点位置while(cur != null) {if(cur.key < key) {curParent = cur;cur = cur.right;}else if(cur.key > key) {curParent = cur;cur = cur.left;}else {break;}}if(cur == null) {return true;//节点不存在}TreeNode del = cur;//待删除节点TreeNode delParent = curParent;//待删除节点的父亲节点//找到待删除节点左子树的最大值(即左子树最右边的元素),用来替换法1待删除节点值if(del.left == null) {//待删除节点无左子树if(del.key > delParent.key) {delParent.right = del.right;}else if(del.key < delParent.key){delParent.left = del.right;}else {//此时,待删除节点为根节点root = root.right;}}else {//找到待删除节点左子树的最大值(即左子树最右边的元素),用来替换待删除节点值curParent = cur;cur = cur.left;while(cur.right != null) {curParent = cur;cur = cur.right;}del.key = cur.key;if(curParent == del) {//此时,待删除节点的左子树根节点无右子树curParent.left = cur.left;}else {curParent.right = cur.left;}}return true;}

3. 二叉搜索树时间复杂度分析

  • 在最优情况下,即二叉搜索树是一棵完全二叉树,时间复杂度是O(log2N)
  • 在最坏情况下,即二叉搜索树是一棵单支二叉树,时间复杂度是O(N)

最优情况:
在这里插入图片描述
最坏情况:
在这里插入图片描述

4. TreeMap和TreeSet

TreeMap和TreeSet底层都是一棵红黑树,而红黑树是一棵近似平衡的二叉搜索树。TreeMap是Map接口的一个实现类,TreeSet是Set接口的一个实现类

5. Map

Map是一个接口,其实现需要依靠TreeMap类或者HashMap类。Map里存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复。这就类比于一个集合,这个集合里放的都是具有两个元素的集合,例如:{{“abc”, 2}, {“cde”, 3}}

5.1 Map的常用方法

  1. V put (K key, V value)
    用于往map中存放键值对

演示1:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);System.out.println(map);//Map类可以直接输出}
}

输出结果:
在这里插入图片描述
演示2:
在这里插入图片描述
map类存储的key是不能重复的,但往map里put同一个key,是可以的,只是在key里依然只会保存一个key,只是key对应的value会发生更新

  • 如果key不存在,会将key-value的键值对插入到map中,返回null
  • 如果key存在,会使用value替换原来key所对应的value,返回旧value
  • put(key,value): 注意key不能为空,但是value可以为空。 key如果为空,会抛出空指针异常
  1. V get(Object key)
    用于得到key对应的value

演示:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);Integer val = map.get("cde");System.out.println(val);}
}

输出结果为:3

注意: 该方法接收返回值时,建议使用包装类而不是基本数据类型,因为使用基本数据类型会发生自动拆箱,会调用intValue方法,假如该key不存在,就会返回null,这时如果进行拆箱的话,就会出现空指针异常
在这里插入图片描述
在这里插入图片描述
3. V getOrDefault(Object key, V defaltValue)
该方法与get方法的不同之处在于有一个默认值,如果key存在,则返回key对应的value值;如果key不存在,就会返回这个默认值

演示:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);Integer val = map.getOrDefault("cde2",-1);System.out.println(val);}
}

输出结果为:-1

  1. V remove(Object key)
    删除key对应的映射关系

演示:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);System.out.println("删除前:" + map);map.remove("abc");System.out.println("删除后:" +map);}
}

输出结果:
在这里插入图片描述

  1. Set<K> keySet()
    返回所有的key的不重复集合,keySet()是将map中的key放在set中返回的

演示:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);// 打印所有的keySet<String> set = map.keySet();System.out.println(set);for(String s : set) {System.out.print(s + " ");}}
}

输出结果:
在这里插入图片描述

  1. Collection<V> values()
    返回所有value的可重复集合,values()是将map中的value放在collect的一个集合中返回的

演示:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);// 打印所有的valueCollection<Integer> collection = map.values();System.out.println(collection);for(Integer x: collection) {System.out.print(x + " ");}}
}

输出结果:
在这里插入图片描述

  1. boolean containsKey(Object key)
    判断是否包含key

演示:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);boolean flg1 = map.containsKey("abc");boolean flg2 = map.containsKey("cba");System.out.println(flg1);System.out.println(flg2);}
}

输出结果:
在这里插入图片描述

  1. boolean containsValue(Object value)
    判断是否包含value

演示:

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8);boolean flg1 = map.containsValue(1);boolean flg2 = map.containsValue(3);System.out.println(flg1);System.out.println(flg2);}
}

输出结果:
在这里插入图片描述

  1. Set<Map.Entry<K, V>> entrySet()
    返回所有的 key-value 映射关系,entrySet()将Map中的键值对放在Set中返回了

注意:

  • Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入
  • 在TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,因为插入键值对时,会在二叉搜索树里进行比较(因此插入的key元素类型要可比,或者在key类中实现了比较方式),将新节点插入到合适位置,而value可以为空。但是HashMap的key和value都可以为空,因为插入时不涉及比较。因此,输出时,如果是TreeMap实现的,则是有序的;若是HashMap实现的,则是无序的

5.2 Map.Entry<K,V>

Map.Entry<K, V> 是Map内部实现的用来存放<key, value>键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式

  1. K getKey()
    返回entry中的key

  2. V getValue()
    返回entry中的value

  3. V setValue(V value)
    将键值对中的value值替换成指定value

通过该方法转化,既可以获取key,也可以获取value;而Map中的get方法只能获取key所对应的value值

public class Test {public static void main(String[] args) {Map<String,Integer> map = new TreeMap<>();map.put("fbc",2);map.put("cde",3);map.put("abc",5);map.put("grh",8); 打印所有的键值对Set<Map.Entry<String,Integer>> entrySet = map.entrySet();System.out.println(entrySet);System.out.println("========");for (Map.Entry<String,Integer> entry:entrySet) {System.out.println("key:" + entry.getKey()+" val:" + entry.getValue());}}
}

输出结果:
在这里插入图片描述

6. Set

Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key,且Key唯一。Set类似于一个集合,具有互异性,最大的作用就是去重。Set的实现依靠TreeSet、HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。

6.1 Set的常用方法

  1. boolean add(E e)
    用来给集合添加元素,但不会添加重复元素

演示1:

    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(4);set.add(7);set.add(1);set.add(3);System.out.println(set);}

输出结果:
在这里插入图片描述

演示2:

    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(1);set.add(7);set.add(1);set.add(3);System.out.println(set);}

输出结果

在这里插入图片描述
和Map类似,Set也可以add集合中已有的元素,但不会将其加入集合中,从而达到去重的效果

  1. void clear()
    清空集合中的元素
    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(4);set.add(7);set.add(1);set.add(3);System.out.println(set);set.clear();System.out.println(set);}

输出结果:
在这里插入图片描述

  1. boolean contains(Object o)
    判断o是否在集合中
    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(4);set.add(7);set.add(1);set.add(3);System.out.println(set);System.out.println(set.contains(4));System.out.println(set.contains(5));}

输出结果:
在这里插入图片描述

  1. Iterator iterator()
    返回迭代器
    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(4);set.add(7);set.add(1);set.add(3);Iterator<Integer> it = set.iterator();while(it.hasNext()) {System.out.print(it.next()+" ");}}

输出结果:
在这里插入图片描述

  1. boolean remove(Object o)
    删除集合中的o
    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(4);set.add(7);set.add(1);set.add(3);System.out.println(set);set.remove(4);System.out.println(set);}

输出结果:
在这里插入图片描述

  1. int size()
    返回set中的元素个数
    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(4);set.add(7);set.add(1);set.add(3);System.out.println(set.size());}

在这里插入图片描述

  1. boolean isEmpty()
    判断集合set是否为空,为空则返回true,反之返回false
    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();System.out.println(set.isEmpty());set.add(4);set.add(7);set.add(1);set.add(3);System.out.println(set.isEmpty());}

输出结果:
在这里插入图片描述

  1. Object[] toArray()
    将set中的元素转换为数组返回
    public static void main(String[] args) {Set<Integer> set = new TreeSet<>();set.add(4);set.add(7);set.add(1);set.add(3);System.out.println(Arrays.toString(set.toArray()));System.out.println("========");Object[] arr = set.toArray();for (Object o : arr) {System.out.print(o + " ");}}

输出结果:
在这里插入图片描述

  1. boolean containsAll(Collection<?> c)
    检查集合c中的元素是否在set中全部存在,是则返回true,否则返回false
    public static void main(String[] args) {Collection<Integer> c = new ArrayList<>();c.add(1);c.add(2);c.add(3);Set<Integer> set = new TreeSet<>();set.add(1);set.add(2);System.out.println(set.containsAll(c));set.add(3);System.out.println(set.containsAll(c));}

输出结果:
在这里插入图片描述

  1. boolean addAll(Collection<?extends E> c)
    将集合c中的元素添加到set中,可以达到去重的效果
    public static void main(String[] args) {Collection<Integer> c = new ArrayList<>();c.add(1);c.add(2);c.add(1);c.add(3);c.add(2);c.add(4);System.out.println(c);Set<Integer> set = new TreeSet<>();set.addAll(c);System.out.println(set);}

输出结果:
在这里插入图片描述

注意:

  • Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
  • TreeSet中不能插入null的key,若插入null,则会抛出空指针异常;而HashSet可以

LeetCode_614">LeetCode刷题

1. 二叉搜索树与双向链表

题目链接:将二叉搜索树转化为排序的双向链表

解题思路:

  1. 对该二叉搜索树采取中序遍历,先找到二叉搜索树最左边的节点
  2. 定义两个引用prev和head,prev用来引用当前pRootOfTree的前一个节点,head始终引用原二叉搜索树最左边的节点,用于返回转化成的双向链表

代码如下:

public class Solution {public TreeNode prev;public TreeNode head;public TreeNode Convert(TreeNode pRootOfTree) {if (pRootOfTree == null) {return null;}Convert(pRootOfTree.left);if (prev == null) {prev = pRootOfTree;head = pRootOfTree;} else{prev.right = pRootOfTree;pRootOfTree.left = prev;prev = pRootOfTree;}Convert(pRootOfTree.right);return head;}
}

http://www.ppmy.cn/ops/87453.html

相关文章

Python面试宝典第25题:括号生成

题目 数字n代表生成括号的对数&#xff0c;请设计一个函数&#xff0c;用于能够生成所有可能的并且有效的括号组合。 备注&#xff1a;1 < n < 8。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))","(()())","(())()"…

POI 快速入门 Excel导入导出

Excel导入导出 1 什么是POI POI简介&#xff08;Apache POI&#xff09;&#xff0c;Apache POI是Apache软件基金会的开放源码函式库&#xff0c;POI提供API给Java程序对Microsoft Office格式档案读和写的功能。 Apache POI官网http://poi.apache.org/ HSSF &#xff0d; 提…

数据灾备及时恢复应急预案

第一节总则 1&#xff0c;灾难备份的目的 为了规范本所重要数据备份清单的建立&#xff0c;备份的职责&#xff0c;备份的检查。以及系统受到破坏后的恢复工作&#xff0c;合理防范计算机及信息系统使用过程中的风险&#xff0c;特制定本预案。 2&#xff0c;灾难恢复的定义 灾…

数据挖掘-数据预处理

来自&#x1f96c;&#x1f436;程序员 Truraly | 田园 的博客&#xff0c;最新文章首发于&#xff1a;田园幻想乡 | 原文链接 | github &#xff08;欢迎关注&#xff09; 文章目录 3.3.1 数据的中心趋势平均数和加权平均数众数&#xff0c;中位数和均值描述数据的离散程度 &a…

Java实战 - 查找最长递增子序列

作者&#xff1a;逍遥Sean 简介&#xff1a;一个主修Java的Web网站\游戏服务器后端开发者 主页&#xff1a;https://blog.csdn.net/Ureliable 觉得博主文章不错的话&#xff0c;可以三连支持一下~ 如有疑问和建议&#xff0c;请私信或评论留言&#xff01; 前言 在计算机科学中…

同步交互与异步交互:深入解析与选择

同步交互与异步交互&#xff1a;深入解析与选择 1、同步交互2、异步交互3、选择策略 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在软件开发的世界里&#xff0c;交互方式主要分为两大类&#xff1a;同步与异步。下面是对这两种方式的解…

有http了为何还要用socket通讯

文章目录 应用场景区别总结 HTTP和WebSocket是两种不同的协议&#xff0c;‌它们各自有不同的用途和优势&#xff0c;‌因此即使有了HTTP&#xff0c;‌还需要WebSocket进行通讯。‌ 应用场景区别 HTTP是一种无状态的、‌单向的协议&#xff0c;‌主要用于从服务器获取信息&…

前后端完全分离实现登录和退出

前后端分离的整合 使用springsecurity前端项目redis完成认证授权的代码 1. 搭建一个前端工程 使用 vue ui搭建&#xff0c;使用webstrom操作 2. 创建一个登录页面 <template><div class"login_container"><!-- 登录盒子 --><div class"l…