第7章 神奇的树
第1节 开启“树”之旅
不含回路的连通无向图。
现实中的族谱、操作系统中的“目录”。
树的相关概念:略略略
第2节 二叉树
二叉树是每个节点最多有两个儿子的树。
满二叉树、完全二叉树(满二叉树的一部分)
完全二叉树存储规律:只要用一个一维数组就可以存储完全二叉树,将完全二叉树进行从上到下,从左到右编号,,可以发现如果父节点的编号是k,那么它的子节点的编号是2k和2k+1。若子节点是x,父节点就是x/2。
第3节 堆–神奇的优先队列
堆是一种特殊的完全二叉树,所有的父节点比子节点小。称为最小堆。如果所有的父节点比子节点大,则称为最大堆。
最小堆的操作:
void siftdown(int i) { //从i向下调整(i插入一个数后)
int flag = 0;
int t;
while(i*2<=n && flag==0) {
if(h[i]>h[i*2]) {
t = i*2;
}else {
t=i;
}
if(i*2+1 <= n) {
if(h[t]>h[i*2+1]) {
t = i*2+1;
}
}
if(t!=i) {
int k = h[t];
h[t] = h[i];
h[i] = k;
i = t;
}else {
flag = 1;
}
}
}
向上调整(插入末尾)
void siftup(int i) {//从i向上调整
int flag = 0;
if(i==1) return;
while(i!=1 && flag==0) {
if(h[i]<h[i/2]) {
int kk = h[i];
h[i] = h[i/2];
h[i/2]=kk;
}else {
flag = 1;
}
i = i/2;
}
}
建立堆
//建立堆
void create() {
for(int i=n/2;i>=1;i--) {
siftdown(i);
}
}
完整的对排序:
package ch7;
import java.util.Scanner;
public class Test1 {
int[] h = new int[101];
int n;
void siftdown(int i) { //从i向下调整
int flag = 0;
int t;
while(i*2<=n && flag==0) {
if(h[i]>h[i*2]) {
t = i*2;
}else {
t=i;
}
if(i*2+1 <= n) {
if(h[t]>h[i*2+1]) {
t = i*2+1;
}
}
if(t!=i) {
int k = h[t];
h[t] = h[i];
h[i] = k;
i = t;
}else {
flag = 1;
}
}
}
void siftup(int i) {//从i向上调整
int flag = 0;
if(i==1) return;
while(i!=1 && flag==0) {
if(h[i]<h[i/2]) {
int kk = h[i];
h[i] = h[i/2];
h[i/2]=kk;
}else {
flag = 1;
}
i = i/2;
}
}
//建立堆
void create() {
for(int i=n/2;i>=1;i--) {
siftdown(i);
}
}
//堆排序,每次删除顶部元素
int deletetop() {
int t;
t = h[1];
h[1] = h[n];
n--;
siftdown(1);
return t;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
Test1 t1 = new Test1();
for(int i=1;i<=num;i++) {
t1.h[i] = sc.nextInt();
}
t1.n = num;
t1.create();
for(int i=1;i<=num;i++) {
System.out.print(t1.deletetop()+ " ");
}
}
}
另一种堆排序的方法:建立最大堆,h[1]是最大的数,每次把h[1]和h[n]交换,h[n]就是最大的数,然后n–,在从h[1]向下调整。直到堆只剩下一个数。(最大堆和最小堆类似,只是最大堆的父结点大于子节点)
void heapsort(){
while(n>1){
swap(1,n);
n--;
siftdown(1);//
}
}
第4节 擒贼先擒王–并查集
有n个强盗,m条线索,每条线索给出的是x号强盗和y号强盗是同伙,求一共有多少个犯罪团伙。
//测试数据
10 9
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
//结果3
package ch7;
import java.util.Scanner;
public class Test2 {
int[] f = new int[1000];
int n,m,k,sum;
//初始化f
void init() {
for(int i=1;i<=n;i++) {
f[i] = i;
}
}
//找爹的递归函数
int getf(int v) {
if(f[v]==v) {
return v;
}else {
f[v] = getf(f[v]);//路径压缩,每次函数返回时,顺便将遇到的人的Boss改为最后找到的祖宗编号
return f[v];
}
}
//合并子集(同伙)
void merge(int v,int u) {
int t1 = getf(v);
int t2 = getf(u);
if(t1 != t2) {
f[t2] = t1; //靠左原则,合并到左边
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Test2 t2 = new Test2();
t2.n = sc.nextInt();
t2.init();
int m = sc.nextInt();
for(int i=1;i<=m;i++) {
int x = sc.nextInt();
int y = sc.nextInt();
t2.merge(x, y);
}
for(int i=1;i<=t2.n;i++) {
if(t2.f[i]==i) {
t2.sum++;
}
}
System.out.println(t2.sum);
}
}