数据结构基础
一、 树
1.生成最小高度的树
给一个排好序的数组 ,生成最小高度的树
private TreeNode creatMinBST(int[]arr,int begin,int end){
if(begin>end) return null;
int mid=(begin+end)/2;
TreeNode res=new TreeNode(arr[mid]);
res.left=creatMinBST(arr,begin,mid-1);
res.right=creatMinBST(arr,mid+1,end);
return res;
}
2.求树的高度
int getHeight(TreeNode node){
if(node==null) return 0;
/**if(node.left==null&&node.right==null) return 1;
如果这样写,那么只有一个孩子的节点会在下面的return里报错*/
return 1+Math.max(getHeight(node.left),getHeight(node.right));
}
一个node都有对应height,可以维护一个map减少重复计算
int getHeight(TreeNode node,Map<TreeNode,Integer> heightMap){
if (node==null) return 0;
if (heightMap.containsKey(node))return heightMap.get(node);
if (node.right==null&&node.left==null){heightMap.put(node,1);return 1}
else {
int hei=Math.max(getHeight(node.right,heightMap),getHeight(node.left,heightMap));
heightMap.put(node, 1 + hei);
}
return getHeight(node);
}
3.检查是否为平衡树
Boolean isBalance(TreeNode node){
//检查平衡树思路:左右高度之差小于一,递归地检查左右子树,满足所有条件才返回true
if (node==null) return true;
boolean b=Math.max(getHeight(node.right),getHeight(node.left))<=1;
return b&&isBalance(node.left)&&isBalance(node.right);
}
4.返回树一条路径所有节点组成的字符串
List<String> list=new LinkedList<>();
void nodePath(String pre,TreeNode<Integer> node){
pre+=node.value;
if (node.right==null&&node.left==null) {
list.add(pre);
//走另一条路并没有上一条路的痕迹哦
return;
}
if (node.left!=null) nodePath(pre,node.left);
if (node.right!=null)nodePath(pre,node.right);
//主方法中调用nodePath("",root)即可
}
5.求树的某个节点高度(非二叉树)
int getHeight(TreeNode node){
if(x.children==null) return 1;
int h=0;
for(int i=0;i<x.children.size();i++)
h=Math.max(h,getHeight(x.children.get(i));
return h;
}
6.二叉树的层次遍历(bfs)
用队列,每出队一个,便入队他的左右节点,直到队列为空
List<TreeNode> nodeList=new LinkedList<>();
List<TreeNode> treeLevel(TreeNode node ){
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(node);
while (!queue.isEmpty()){
nodeList.add(queue.poll());
if (node.left!=null)queue.offer(node.left);
if (node.right!=null)queue.offer(node.right);
}
return nodeList;
}
7.给一个层数,返回该层的所有节点
void dfs(TreeNode node,int dep){
if (node==null) return;
if (getHeight(node)==dep)
nodeList.add(node);
// if (node.left!=null )
dfs(node.left,dep);
// if (node.right!=null) 等于null也没事会自动return
dfs(node.right,dep);
}
8. BST(二叉树)的中序遍历
List<TreeNode> list=new LinkedList<>();
void inorder(TreeNode node){
if (node==null) return;
inorder(node.left);
list.add(node);
inorder(node.right);
}
9.BST里最大(小)的节点
TreeNode maxOfBST(TreeNode node){
while (node.right!=null)
node=node.right;
return node;
}
10.用堆栈代替递归实现BST的中序遍历
List<TreeNode> list2=new LinkedList<>()
void stackInordor(TreeNode node){
Stack<TreeNode> stack = new Stack<>();
while (node!=null||!stack.empty()){
while (node!=null){
stack.add(node);
node=node.left;
}
TreeNode pop=null;
if(!stack.empty()){
pop=stack.pop();
list2.add(pop);
}
//if (pop.right!=null)可加可不加,如果为空,大while进来后直接弹出栈顶
//此时node为栈顶右孩子
node=pop.right;
}
}
11.在一个树中找两个节点的最近公共祖先
//判断一个树中是否包含某个节点
Boolean treeContain(TreeNode x,TreeNode node){
if (node==null) return false;
if (x==node) return true;
return treeContain(x.left,node)||treeContain(x.right,node);
}
//在一个树中找两个节点的最近公共祖先
/*还有一种思路:建两个节点到root的列表,同时倒叙遍历,返回第一个不相同节点的父节点*/
private TreeNode lowestCommonAncestor(TreeNode node,TreeNode p,TreeNode q){
if (node==null) return null;
if (node.equals(p)||node.equals(q)) return node;
//如果为父子关系,返回父亲
Boolean pOnLeft=treeContain(node.left,p);
Boolean qOnLeft=treeContain(node.left,q);
if (qOnLeft!=pOnLeft)
return node;//如果分局节点两侧,返回该节点
else if (pOnLeft)
return lowestCommonAncestor(node.left,p,q);//都在左边,用左孩子递归
else
return lowestCommonAncestor(node.right,p,q); //都在右边,用右孩子递归
}
12.输出和为指定值的路径
List<TreeNode> nodeList=new LinkedList<>();
List<TreeNode> nodePath(int i, TreeNode node,int target) {
i += (int) node.value;
if (node.right == null && node.left == null) {
nodeList.add(node);
if (i==target) return nodeList ;
else return null;
}
if (node.left != null) nodePath(i, node.left,target);
if (node.right != null) nodePath(i, node.right,target);
return null;
}
//以下为另一种方法
LinkedList<List<TreeNode>> list_listOfTreeNode=new LinkedList<>();
void nodePath(List<TreeNode> pre,TreeNode node,int target){
pre.add(node);
if (node.right == null && node.left == null){
if ((int)node.value-target==0)
list_listOfTreeNode.add(pre);
}
if (node.left != null)
nodePath(pre, node.left,target-(int)node.value);
if (node.right != null)
nodePath(pre, node.right,target-(int)node.value);
}
13.中序遍历中寻找后继节点
TreeNode findSuccessor(TreeNode node){
if (node.right!=null)
return minOfBST(node.right);
else {
while (node!=node.parent.left&&node.parent!=null)
//防止一直没找到而无限循环node.parent==null
//(到根节点都没找到)会退出循环循环
node=node.parent;
return node.parent;
}
}
二、图
八连块的个数
public class 八个方向的连块 {
private static char [][]data={
"*@@*@".toCharArray(),
"**@*@".toCharArray(),
"****@".toCharArray(),
"@@@*@".toCharArray(),
"@@**@".toCharArray(),
};
static int count=0;
//static int[][]visited=new int[data.length][data[0].length];
static void dfs(int row,int column){
if (row<0||row>=data.length||column<0||column>=data[0].length)
return;
if (data[row][column]=='*')
return;//不是@或已经被扫描过了变成了@
//if(visited[row][column]==1)
// return;
data[row][column]='*'; //如果为@
//visited[row][column]=1;
dfs(row,column+1);
dfs(row,column-1);
dfs(row+1,column-1);
dfs(row+1,column+1);
dfs(row+1,column);
dfs(row-1,column);
dfs(row-1,column-1);
dfs(row-1,column+1);
//visited[row][column]=0;
}
public static void main(String[] args) {
for (int i = 0; i <data.length ; i++) {
for (int j = 0; j <data[0].length ; j++) {
// if (visited[i][j]==0&&data[i][j]=='@')
//用标记的话可以代替
if (data[i][j]=='@'){
dfs(i,j);count++;
}
}
}
System.out.println(count);
}
}
判断一点跟否联通另一个点
private static Boolean check(int[][]graph,int[][]visited,int a,int b,int c,int d){
if (a==c&&b==d)
return true;
int value=graph[a][b]
int at=a;
int bt=b;
Boolean left=false;
Boolean right=false;
Boolean up=false;
Boolean down=false;
if (a-1>=0&&visited[a-1][b]==0&&graph[a-1][b]==value) {
visited[a-1][b]=1;
left=check(graph,visited,a-1,b,c,d);
//回溯,向右走不收向左走影响
visited[a-1][b]=0;
a=at;
b=bt;*
}
if (a+1<graph.length&&visited[a+1][b]==0&&graph[a+1][b]==value) {
visited[a+1][b]=1;
right=check(graph,visited,a+1,b,c,d);
//回溯,向右走不收向左走影响
visited[a+1][b]=0;
a=at;
b=bt;
}
if (b-1>=0&&visited[a][b-1]==0&&graph[a][b-1]==value) {
visited[a][b-1]=1;
up=check(graph,visited,a,b-1,c,d);
//回溯,向右走不收向左走影响
visited[a][b-1]=0;
a=at;
b=bt;
}
if (b+1>=0&&visited[a][b+1]==0&&graph[a][b+1]==value) {
visited[a][b+1]=1;
down=check(graph,visited,a,b+1,c,d);
//回溯,向右走不收向左走影响
visited[a][b+1]=0;
// a=at;
//b=bt;
}
return left||right||up||down;
}
返回图的拓扑排序
/**
*
* @date 2019/7/26 10:11
*/
public class 图的拓扑排序 {
static final int n=4;
static int[][]graph={//有向图的邻接矩阵表示法
{0,1,0,0},
{0,0,0,0},
{0,1,0,0},
{0,0,1,0}
};
static int []visited=new int[n];
//访问状态:1,上一次dfs访问过的;0:,从未被访问;-1,本次dfs里访问过的(防止出现环路)
static int []topo=new int[n];
private static int t=n;
//结果存放的数组
public static void main(String[] args) {
for (int i = 0; i <n ; i++) {
if (visited[i]==1)continue;
if (!dfs(i)){
System.out.println(false);//没有拓扑序列
return;
}
}
for (int i = 0; i <n ; i++) {
System.out.println(topo[i]);
}
}
private static boolean dfs(int i) {
visited[i]=-1;
for (int j = 0; j <n ; j++) {
if (graph[i][j]>0){
if (visited[j]==-1) return false;//有环路
if (visited[j]==0&&dfs(j)==false) return false;
}
}
//此时走过了该节点,且没有发生return,即有拓扑序
topo[--t]=i;//0——>1时,是1的dfs先走完,先执行这句话,所以把1放在最后
//不能写n,如果写n,在执行一次dfs后。n会变化
visited[i]=1;
return true;
}
}
欧拉道路
import java.util.Stack;
/**
*
* @date 2019/7/26 12:27
*/
public class 欧拉道路 {
static Stack<String> stack=new Stack<>();
static int[][]gragh={//无向图,数字表示桥的数目,斜对称
{0,1,2,1},
{1,0,0,0},
{2,0,0,1},
{1,0,1,0},
};
static int n=4;//四个节点0,1,2,3
static int[][] visited=new int[n][n];
/**
*
* @param i 当前节点
*/
static void euler(int i){
for (int j = 0; j <n ; j++) {
if (gragh[i][j]>0&&visited[i][j]<gragh[i][j]){
visited[i][j]++;
visited[j][i]++;//如果只写上面,对面还可以走过来
euler(j);
stack.add(i+"->"+j);//最先执行这句话的是最后一条边,但是输出要从前开始
}
}
}
public static void main(String[] args) {
euler(1);//起点和终点只能是奇数边的节点
while (!stack.empty())
System.out.println(stack.pop());
}
}
是否为二分图
import java.util.LinkedList;
import java.util.List;
/**
* @author 欧阳煜
* @date 2019/7/25 18:03
*/
public class GraphNode {
int value;
List<GraphNode> neighbors=null;
Boolean checked=false;
public GraphNode(int value) {
this.value = value;
}
void add(GraphNode node) {
if (this.neighbors == null) this.neighbors = new LinkedList<>();
this.neighbors.add(node);
}
public int size(){
return this.neighbors.size();
}
}
/**
* @date 2019/7/26 13:06
*/
public class 二分图 {
static class TwoGraphNode extends GraphNode{
int color;
public TwoGraphNode(int value) {
super(value);
}
}
static Boolean dfs(TwoGraphNode node,int c){
node.color=c;
for (int i = 0; i <node.size() ; i++) {
TwoGraphNode neighbor= (TwoGraphNode) node.neighbors.get(i);
if (neighbor.color==c) return false;
if (neighbor.color==0) {
Boolean dfs = dfs(neighbor, -c);//如果没有染色,尝试染个相反色
if (dfs==false) return false;//如果一个染色不成功,整个就不成功
}
}
return true;//全部走完都没有return的,就是一个二分图
}
public static void main(String[] args) {
TwoGraphNode n1=new TwoGraphNode(1);
TwoGraphNode n2 = new TwoGraphNode(2);
TwoGraphNode n3 = new TwoGraphNode(3);
TwoGraphNode n4 = new TwoGraphNode(4);
// GraphNode n1 = new GraphNode(1);
// GraphNode n2 = new GraphNode(1);
// GraphNode n3 = new GraphNode(1);
// GraphNode n4 = new GraphNode(1);
n1.add(n2);
n1.add(n4);
n2.add(n3);
n2.add(n1);
n3.add(n2);
n3.add(n4);
n4.add(n1);
n4.add(n3);
System.out.println(dfs(n1,1));
}
}
最小生成树
import java.util.*;
/**
*
* @date 2019/7/26 16:42
*/
public class 最小生成树 {
static List<Edge> edgeList;
static int n;//顶点数
static Set<Edge> set=new HashSet<>();
private static TreeNode findRoot(TreeNode node){
while (node.parent!=null){
node=node.parent;//左边表示当前node所在位置
}
return node;
}
public static void union(TreeNode x,TreeNode y){
findRoot(y).parent=findRoot(x);
//p=p.parent类比,左边的指向右边
}
public static void main(String[] args) {
edgeList.add(new Edge("2","3",3));
//省略
Collections.sort(edgeList);
// 实现Comparable接口,根据distance比较
for (Edge e:edgeList) {
TreeNode x=(TreeNode) e.start;
TreeNode y=(TreeNode) e.end;
if(findRoot(x)!=findRoot(y)){
union(x,y);
set.add(e);
}
else
continue;
if (set.size()==n-1)
return;
}
System.out.println(set);
}
}
Dijkstra最短路
**
思路:
**
/*初始化d[]
- while 还有没被访问的
- 记录d【】里最小距离的节点i,并记为及访问v.add
- 依次找邻居j
-
如果d[i]+i到j<d[j] d[j]=d[i]+ij
-
并记j的上一节点为i:pre[j]=i
- 输出到给定终点的路径:
- while(end不为起点)
-
stack.add (pre[end]--->end)
-
end=pre[end]
- 弹光stack
- */
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/**
* @author 欧阳煜
* @date 2019/7/27 10:26
*/
public class Dijkstra最短路 {
public static void main(String[] args) {
int []ans=shortestPathArray(0);
getPathTo(4);
while (!stack.empty()){
System.out.println(stack.pop());
}
System.out.println("shortest :"+ans[4]);
}
static Stack<String> stack=new Stack<>();
static void getPathTo(int end){
while (end!=0){
stack.add((char)(prev[end]+'A')+" to "+(char) (end+'A')+" : "+graph[prev[end]][end]);
end=prev[end];
}
}
static int []prev;
//最后返回一个数组,记录着起点到各个点的最短路径长,但要打印路径必须记录上一步经过哪个节点
static int [][]graph={
{0,7,3,0,0},
{7,0,1,2,6},
{3,1,0,2,0},
{0,2,2,0,4},
{0,6,0,4,0},
};
static int[] shortestPathArray(int start){
int number=graph.length;
prev=new int[number];
prev[start]=start;//源节点的前一个节点是自身
int distance[]=new int[number];//始终是起点到0,1,2,3,4节点的距离
// boolean visited[]=new boolean[number];
// visited[start]=true;//记为已访问过
List<Integer> visited=new LinkedList<>();
// visited.add(start);
/* for (int neighbor = 0; neighbor <number ; neighbor++) {
if (neighbor!=start&&graph[start][neighbor]==0)
distance[neighbor]=100;//不直接可达,起点到这个节点距离
if (neighbor!=start&&graph[start][neighbor]>0){
distance[neighbor]=graph[start][neighbor];
prev[neighbor]=start;
}
}*/
// 以上代码与下面重复了,把distance初始化好之后,就可以执行下面了
for (int i = 0; i <distance.length ; i++) {
distance[i]=Integer.MAX_VALUE;
}
distance[start]=0;//自己到自己为0
while (visited.size()<number){//到全部节点都访问过,return distance数组
int node=minNode(distance,visited); //在未访问中找到距离起点最近的节点
visited.add(node);
for (int neighbor = 0; neighbor <distance.length ; neighbor++) {
//在所有节点中找node的邻居
int cost=graph[node][neighbor];
if (cost>0&&cost+distance[node]<distance[neighbor]){
distance[neighbor]=cost+distance[node];
//到neighbor从node走更近
prev[neighbor]=node;
}
}
}
return distance;
}
//返回数组最小元素下标,即节点,而不是最小元素!!!!
private static int minNode(int[] distance, List<Integer> visited) {
int index=1;
int min=distance[1];
for (int i = 0; i <distance.length ; i++) { //遍历所有节点
if (!visited.contains(i)&&distance[i]<min){
min=distance[i];
index=i;
}
}
return index ;
}
}
bfs 四个瓶子倒酒次数
/**
*
* @date 2019/7/27 17:21
*/
import java.util.LinkedList;
import java.util.*;
/**
* 有4个红酒瓶子,它们的容量分别是:9升, 7升, 4升, 2升
* 开始的状态是 [9,0,0,0],也就是说:第一个瓶子满着,其它的都空着。
*
* 允许把酒从一个瓶子倒入另一个瓶子,但只能把一个瓶子倒满或把一个瓶子倒空,不能有中间状态。
* 这样的一次倒酒动作称为1次操作。
*
* 假设瓶子的容量和初始状态不变,对于给定的目标状态,至少需要多少次操作才能实现?
* 本题就是要求你编程实现最小操作次数的计算。
*
* 输入:最终状态(空格分隔)
* 输出:最小操作次数(如无法实现,则输出-1)
*
* 例如:
* 输入:
* 9 0 0 0
* 应该输出:
* 0
*
* 输入:
* 6 0 0 3
* 应该输出:
* -1
*
* 输入:
* 7 2 0 0
* 应该输出:
* 2
*
*/
class Node{
int depth;
String s;
public Node(int depth, String s) {
this.depth = depth;
this.s = s;
}
public Node(String s) {
this.s = s;
}
public int[] getState() {
String[] t=s.split("");
int[] intt=new int[t.length];
for (int i = 0; i <t.length ; i++) {
intt[i]=Integer.parseInt(t[i]);
}
return intt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node node = (Node) o;
//return depth == node.depth && s.equals(node.s);
// return s.equals(node.s);
return s.equals(node.s);
}
@Override
public int hashCode() {
// return Objects.hash(depth, s);
// return s.hashCode();
return Objects.hash(s);
}
}
public class 四个瓶子倒酒次数 {
static int[]capacity={9,7,4,2};
static Queue<Node> queue=new LinkedList<>();
static Set<Node> set=new HashSet<>() ;
public static void main(String[] args) {
Node begin=new Node(0,"9000");
// begin.depth=0;
Scanner scanner=new Scanner(System.in);
String ss=""+scanner.nextInt()+scanner.nextInt()
+scanner.nextInt()+scanner.nextInt();
queue.add(begin);
System.out.println(bfs(ss));
}
static void add(Queue<Node> queue,Node node){
if (!set.contains(node)){
set.add(node);
queue.add(node);
}
}
private static int bfs(String ss) {
while (!queue.isEmpty()){
Node now=queue.poll();//弹出一个,add他所有可能的下一个state
boolean flag=true;
if (now.s!=ss) flag=false;
if (flag==true)
// if (now.equals(end))
{
return now.depth;
}
int []state=now.getState();
for (int i = 0; i <state.length ; i++) {//i往j倒酒
if (state[i]>0){
for (int j = 0; j <state.length ; j++) {
if (i==j) continue;//对于每一种合法的ij有两种情况
int jSpace=capacity[j]-state[j];
if (jSpace>=state[i]){//把i倒空
int temp=state[i];
state[j]+=state[i];
state[i]=0;
// Node te=new Node(ArraytoString(state));
// te.depth++;
// add(queue,te);
add(queue,new Node(now.depth+1,arrayToString(state)));
state[i]=temp;//试一次情况后回到母状态,好从母状态试另一种情况
state[j]-=state[i];
}
if (jSpace>0&&jSpace<=state[i]){//把j倒满
int temp=state[i];
state[i]-=jSpace;
state[j]=capacity[j];
add(queue,new Node(now.depth+1,arrayToString(state)));
state[i]=temp;
state[j]=capacity[j]-jSpace;
}
}
}
}
}
return -1;//如果前面一个return都没有,即一个都没匹配上
}
private static String arrayToString(int[] state) {
String s="";
for (int i:state
) {
s+=i+"";
}
return s;
}
}
图的bfs——走出迷宫的最小步数
import java.util.LinkedList;
import java.util.Queue;
import java.util.*;
/**
* @author 欧阳煜
* @date 2019/7/28 8:39
*/
/*
...11111111111111111111111111111
11.111111........1111111111.1111
11.111111..111.11111111.....1111
11.11111111111.1111111111.111111
11.111111.................111111
11.111111.11111111111.11111.1111
11.111111.11111111111.11111..111
11..........111111111.11111.1111
11111.111111111111111.11....1111
11111.111111111111111.11.11.1111
11111.111111111111111.11.11.1111
111...111111111111111.11.11.1111
111.11111111111111111....11.1111
111.11111111111111111111111.1111
111.1111.111111111111111......11
111.1111.......111111111.1111.11
111.1111.11111.111111111.1111.11
111......11111.111111111.1111111
11111111111111.111111111.111...1
11111111111111...............1.1
111111111111111111111111111111..
如上图的迷宫,入口,出口分别:左上角,右下角
"1"是墙壁,"."是通路
求最短需要走多少步?
* */
public class 走出迷宫的最小步数 {
static class Node{
int x;
int y;
int depth;
public Node(int x, int y, int depth) {
this.x = x;
this.y = y;
this.depth = depth;
}
}
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
Queue<Node> queue = new LinkedList<>();
int m=scanner.nextInt();
int n=scanner.nextInt();
String s=scanner.nextLine();//!!把回车当成的字符串去除
char[][] graph=new char[m][n];
int[][] visited=new int[m][n];//也可以把访问过的节点放到一个set里
for (int i = 0; i <m ; i++) {
graph[i]=scanner.nextLine().toCharArray();
}
Node start=new Node(0,0,0);
queue.add(start);
while (!queue.isEmpty()){
Node now=queue.poll();
int x=now.x;
int y=now.y;
int depth=now.depth;
if (x==m-1&&y==n-1){
System.out.println(depth);
break; //走的过程中遇到了终点,就不用再走剩下的路
}
visited[x][y]=1;
if (x-1>=0&&visited[x-1][y]==0&&graph[x-1][y]=='.')
//没有越界,没有访问,没有墙
queue.add(new Node(x-1,y,depth+1));
if (x+1<m&&visited[x+1][y]==0&&graph[x+1][y]=='.')
//没有越界,没有访问,没有墙
queue.add(new Node(x+1,y,depth+1));
if (y-1>=0&&visited[x ][y-1]==0&&graph[x][y-1]=='.')
//没有越界,没有访问,没有墙
queue.add(new Node(x,y-1,depth+1));
if (y+1<n&&visited[x][y+1]==0&&graph[x][y+1]=='.')
//没有越界,没有访问,没有墙
queue.add(new Node(x,y+1,depth+1));
}
}
}