class Solution {
private class BST<E extends Comparable<E>>
{ //这里也是暗含乾坤,必须extends呀
private Node root;
private int size;
private class Node
{
E value;
Node left;
Node right;
public Node(E e)
{
value=e;
left=null;
right=null;
}
}
public boolean contains(E e)
{
return contains(this.root,e);
}
private boolean contains(Node node,E e)
{
if(node==null)
return false;
if(node.value.equals(e)) //如果是引用类型的话,那么就是.equals
return true;
if(node.value.compareTo(e)>0)
return contains(node.left,e);
if(node.value.compareTo(e)<0)
return contains(node.right,e);
return false;
}
public void add(E e)
{
this.size++;
root=add(this.root,e);
}
public int getSize()
{
return this.size;
}
private Node add(Node node,E e) //add函数我写得实在是不行。
{
if(node==null)
{
node=new Node(e);
return node;
}
if(node.value.compareTo(e)>0)
node.left=add(node.left,e);
if(node.value.compareTo(e)<0)
node.right=add(node.right,e);
return node;
}
}
public int uniqueMorseRepresentations(String[] words)
{
// List<String> mosCode=new ArrayList<>();
//之后再想,如果换成是其他的类似数组类型的存储咋整
String []mosCode={".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."};
BST<String> bst=new BST<>();
for(String word:words)
{
StringBuilder s=new StringBuilder();
for(int i=0;i<word.length();i++) //数组是属性,然后字符串是函数。
{
int index=word.charAt(i)-'a';
s.append(mosCode[index]);//这个stringbuilder应该用append
}
if(bst.contains(s.toString())!=true)
{
bst.add(s.toString());
}
}
return bst.getSize();
}
}
里面的小知识点:
1. private class BST<E extends Comparable<E>> 对于bst而言,必须要这句话,因为没有这句话的话,那么E类型的数据未必带有比较的功能,所以bst建立的时候必须写这句
也就是说:如果你的数据必须带有比较功能,那么泛型那里必须写:e extends Comparable<E>
2.if(node.value.compareTo(e)==0 对于每个值,都必须用compareTo函数最好,因为的话你不知道这个值 是不是可以用==号来做,所以,比较大小,泛型最好用compareTo方法。
3.对于add函数的部分,必须要记住。add函数必须返回新添加的空间的地址,这是很重要的一点!不然的话这片空间的开拓是白开拓了。详见下面的代码
public void add(E e)
{
this.size++;
root=add(this.root,e);
}
public int getSize()
{
return this.size;
}
private Node add(Node node,E e) //add函数我写得实在是不行。
{
if(node==null)
{
node=new Node(e);
return node;
}
if(node.value.compareTo(e)>0)
node.left=add(node.left,e);
if(node.value.compareTo(e)<0)
node.right=add(node.right,e);
return node;
}
}
4.上面说的这件事情,就是说add函数,无论是什么东西的add函数,树结构的add函数,都应该这么写。返回新开拓的空间,并且新开拓的空间需要有人来接手。
5.数组类型的话,那么用大括号来做{}
6.什么时候用stringbuilder这种东西呢?
StringBuilder
是可变对象,用来高效拼接字符串;
StringBuilder
可以支持链式操作,实现链式操作的关键是返回实例本身;
StringBuffer
是StringBuilder
的线程安全版本,现在很少使用。
stringbuilder是可变对象,用来拼接字符串,也就是说:拼接频繁的话,那么用stringbuilder更划算。
7.然后的话对stringbuilder的对象进行拼接,用的是append函数。
对于String类型来说,虽然可以直接拼接字符串,但是,在循环中,每次循环都会创建新的字符串对象,然后扔掉旧的字符串。这样,绝大部分字符串都是临时对象,不但浪费内存,还会影响GC效率。
为了能高效拼接字符串,Java标准库提供了StringBuilder
,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder
中新增字符时,不会创建新的临时对象:
StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1000; i++) {
sb.append(',');
sb.append(i);
}
String s = sb.toString();所以说,自己知道了什么时候应该用stringbuilder:多次拼接数据的时候。自己知道了如何拼接,用append函数。8.对于length来说,数组是一个属性length,字符串是一个函数length()。最后,从stringbuilder变为string,用tostring函数即可对这道题目的思考:1.BST到底是什么时候应该去用呢?BST的好处:中序遍历的话,那么值是从小到大排布的了。对于数据的存储,它提供了结构。可以让你知道这个东西是否在其中,以及数据是怎么排布的。2.但是其实,bst到底是不是能用于实战呢?目前我觉得,如果说是从小到大排序的话,那么为什么不用排序算法,拍好了之后插入数组里面呢?存疑,因为这个可能比较麻烦一点,排序完之后还插入 消耗的时间多? 那么从小到大排布的话,那么确实是用bst比较方便 那么作为搜索的结构,set、list都可以实现contains的功能,用这两个岂不是更好?那么set的话可以防止重复,在这里面挺像需要的。3.这里其实用set更好,用set重新实现一下试试。用set成功实现了:
Set<String> set=new HashSet<>();
size是set里面的一个函数,其实的话不知道某个集合里面有什么函数,用.size()函数是可以猜猜的set的话,确实更方便了。
这里是说有排序的这个集合,也可以用TreeSet来实现。
TreeSet是有排序的集合实现。
所以说集合的话,防止了数据的重复,然后的话Treeset还保证了数据的顺序性。
那么这个BST按小到大排序的优点更加没有了,然后contains也能实现
所以说BST是树形结构来的,从左到右,从小到大的树形结构。
那么自己写BST的这个好处,确实好像没什么好处了。那么自己学会了怎么写BST,具体应用的话,好像确实是set之类的比较好。
T102 二叉树的层次遍历:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
// 层次序遍历,那么是栈或者queue,栈 先进后出
//那么是把相应的右孩子先推进栈里面去。
//层次序遍历的问题:每层的数量是不一定的。那么pop出来之后,直到空之后,再进行输入的操作。
Deque<TreeNode> dq=new LinkedList<>();
TreeNode node=root; //这里不仅要输出,而且要告诉它,你的输出是第几层输出的。
dq.push(node);//那么我认为在访问左、访问右孩子的时候做这件事吧
//那么层次序遍历其实没必要啊,没必要用stack啊,我每次都输出四个不就好了
//每一次需要输出几个是说不定的。
//其实突然想到了,如果用栈的话,那么在脑海里面模拟一下栈的过程,其实很有利于自己思考得到结果的。
List<List<Integer>> listResult=new ArrayList<List<Integer>>();//先pop出来,pop直到这个栈是空的,再能够去处理
//pop出来,打印,并且放到数组里面。出口是什么?如果说pop出来的数组是空的话,那么说明添加进去的已经是空的了
List<TreeNode> thisRoundNode=new ArrayList<TreeNode>();
// thisRoundNode.add(root);
boolean first=true;
while(first || !thisRoundNode.isEmpty())
{
first=false;
List<Integer> thisRound=new ArrayList<Integer>();
while(!dq.isEmpty())
{
node=dq.pop(); //每次只pop一个,pop完了都不知道是哪里的人了。
thisRound.add(node.val);//只要不是空的,那么就一直pop,pop完了之后添加到
thisRoundNode.add(node);
}
listResult.add(thisRound);
for(TreeNode n:thisRoundNode)
{
if(n.right!=null)
dq.push(n.right);
if(n.left!=null)
dq.push(n.left);
}
}
return listResult;
}
}
上面是自己写的,自己总结一下:
1.栈这个东西,先进后出,因为每个层级都需要分别开来,那么我引入了list的数组
2.栈这个东西,我认为需要在脑海里面去理顺这个过程到底是怎么样,理顺了之后才能知道到底怎么去写代码。
错在哪里,不知道。
来源:https://www.cnblogs.com/startFrom0/p/12597487.html