**数据结构的分类 **
线性结构和非线性结构
二叉树数据结构
二叉树
二叉树(Binary Tree)是一种树形结构,它的特点是每个节点最多只有两个分支节点,一棵二叉树通常由根节点,分支节点,叶子节点组成。而每个分支节点也常常被称作为一棵子树。
根节点:二叉树最顶层的节点分支节点:除了根节点以外且拥有叶子节点叶子节点:除了自身,没有其他子节点
常用术语在二叉树中,我们常常还会用父节点和子节点来描述,比如图中2为6和3的父节点,反之6和3是2子节点
2、二叉树的性质
性质1:二叉树第i层上的结点数目最多为2^(i-1)(i>=1)
性质2:深度为k的二叉树至多有2^k -1个结点(k>=1)
性质3:包含n个结点的二叉树的高度至少为(log2^n)+1
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1
3、性质4的证明
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1
证明:因为二叉树中所有结点的度数均不大于2,不妨设n0表示度为0的结点个数,n1表示度为1的结点个数,n2表示度为2的结点个数。三类结点加起来为总结点个数,于是便可得到:n=n0+n1+n2 (1)
由度之间的关系可得第二个等式:n=n0x0+n1+n2x2+1即n=n1+2nx2+1 (2) (节点数等于度加1)
将(1)(2)组合在一起可得到n0=n2+1
三、满二叉树、完全二叉树和二叉查找树
1、满二叉树
定义:高度为h,并且由2^h-1个结点组成的二叉树,称为满二叉树
2、完全二叉树
定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下层的叶结点集中在靠左()的若干位置上,这样的二叉树称为完全二叉树。
完全二叉树,可以看做是满二叉树在最后一层从右往左砍掉一些节点。
如果从满二叉树中在最后一层自左向右砍掉的节点数是偶数,那么该完全二叉树中度为1的节点数就是0。
如果砍掉的节点数是奇数,那么该完全二叉树中就有且仅有一个节点的度为1.
面试题:如果一个完全二叉树的结点总数为768个,求叶子结点的个数。
由二叉树的性质知:n0=n2+1,将之带入768=n0+n1+n2中得:768=n1+2n2+1,因为完全二叉树度为1的结点个数要么为0,要么为1,那么就把n1=0或者1都代入公式中,很容易发现n1=1才符合条件。所以算出来n2=383,所以叶子结点个数n0=n2+1=384。
总结规律:如果一棵完全二叉树的结点总数为n,那么叶子结点等于n/2(当n为偶数时)或者(n+1)/2(当n为奇数时)
3、二叉查找树
定义:二叉查找树又被称为二叉搜索树。设x为二叉查找树中的一个结点,x结点包含关键字key,结点x的key值计为key[x]。如果y是x的左子树中的一个结点,则key[y]<=key[x];如果y是x的右子树的一个结点,则key[y]>=key[x]
在二叉查找树中:
(1)若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值。
(2)任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
(3)任意结点的左、右子树也分别为二叉查找树。
(4)没有键值相等的结点。
二叉树的遍历
根据两个遍历写二叉树(前加中或者后加中才可以写出二叉树)
//因为根据前序遍历可以得到A是根节点
将a的左子树单独拿出来分析该子树
用程序实现二叉树遍历的步骤
package 算法;
//二叉树的遍历、节点、二叉树的定义
public class BinaryTreeDemo {
public static void main(String[] args) {
//测试,先创建一颗二叉树
BinaryTree binaryTree = new BinaryTree();
//创建需要的节点
HeroNode node1 =new HeroNode(1,"jack");
HeroNode node2 =new HeroNode(2,"lucy");
HeroNode node3 =new HeroNode(3,"tom");
HeroNode node4 =new HeroNode(4,"gogo");
HeroNode node5 =new HeroNode(5,"dd");
//说明,先手动创建二叉树,以后再学用递归的方法创建二叉树
binaryTree.setRoot(node1);
node1.setLeft(node2);
node1.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
//测试前序遍历
System.out.println("前序遍历");
binaryTree.preOrder();// 1 2 3 5 4
System.out.println("中序遍历");
binaryTree.midOrder(); //2 1 5 3 4
System.out.println("后序遍历");
binaryTree.postOrder();// 2 5 4 3 1
}
}
//先创建 HeroNode节点
class HeroNode{
private int no;
private String name;
private HeroNode left;
private HeroNode right;
public HeroNode(int no,String name){
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
//重写toString()方法,用于输出
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
//编写前序遍历的方法
public void preOrder(){
System.out.println(this);//先输出父节点
//递归向左子树递归前序遍历
if(this.left !=null){
this.left.preOrder();
}
//递归向右子树前序遍历
if(this.right!=null){
this.right.preOrder();
}
}
//编写中序遍历
public void midOrder(){
//递归向左子树中序遍历
if(this.left!=null){
this.left.midOrder();
}
//输出父节点
System.out.println(this);
//递归向右子树中序遍历
if(this.right!=null){
this.right.midOrder();
}
}
// 编写后序遍历
public void postOrder(){
if (this.left!=null){
this.left.postOrder();
}
if(this.right!=null){
this.right.postOrder();
}
System.out.println(this);
}
}
//定义BinaryTree 二叉树
class BinaryTree{
private HeroNode root;
//设置根节点,通过根节点调用节点的方法可以增加节点
public void setRoot(HeroNode root) {
this.root = root;
}
//树的前序遍历
public void preOrder(){
if(this.root!=null){
this.root.preOrder();
}else{
System.out.println("二叉树为空无法遍历");
}
}
//书的中序遍历
public void midOrder(){
if(this.root!=null){
this.root.midOrder();
}else {
System.out.println("二叉树为空无法遍历");
}
}
//后序遍历
public void postOrder(){
if(this.root!=null){
this.root.postOrder();
}else {
System.out.println("二叉树为空无法遍历");
}
}
}
二叉树的前序查找 中序查找 后序查找
package 算法;
public class BinaryTreeDemo {
public static void main(String[] args) {
//测试,先创建一颗二叉树
BinaryTree binaryTree = new BinaryTree();
//创建需要的节点
HeroNode node1 =new HeroNode(1,"jack");
HeroNode node2 =new HeroNode(2,"lucy");
HeroNode node3 =new HeroNode(3,"tom");
HeroNode node4 =new HeroNode(4,"gogo");
HeroNode node5 =new HeroNode(5,"dd");
//说明,先手动创建二叉树,以后用递归的方法创建二叉树
binaryTree.setRoot(node1);
node1.setLeft(node2);
node1.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
//测试前序遍历
System.out.println("前序遍历");
binaryTree.preOrder();// 1 2 3 5 4
System.out.println("中序遍历");
binaryTree.midOrder(); //2 1 5 3 4
System.out.println("后序遍历");
binaryTree.postOrder();// 2 5 4 3 1
//测试前序查找
System.out.println("前序查找");
System.out.println(binaryTree.preOrderSearch(3));
//测试中序查找
System.out.println("中序查找");
System.out.println(binaryTree.midOrderSearch(3));
//测试后序查找
System.out.println("后序查找");
System.out.println(binaryTree.postOrderSearch(3));
}
}
//先创建 HeroNode节点
class HeroNode{
private int no;
private String name;
private HeroNode left;
private HeroNode right;
public HeroNode(int no,String name){
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
//重写toString()方法,用于输出
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
//编写前序遍历的方法
public void preOrder(){
System.out.println(this);//先输出父节点
//递归向左子树递归前序遍历
if(this.left !=null){
this.left.preOrder();
}
//递归向右子树前序遍历
if(this.right!=null){
this.right.preOrder();
}
}
//编写中序遍历
public void midOrder(){
//递归向左子树中序遍历
if(this.left!=null){
this.left.midOrder();
}
//输出父节点
System.out.println(this);
//递归向右子树中序遍历
if(this.right!=null){
this.right.midOrder();
}
}
// 编写后序遍历
public void postOrder(){
if (this.left!=null){
this.left.postOrder();
}
if(this.right!=null){
this.right.postOrder();
}
System.out.println(this);
}
//前序遍历查找
/*no 查找no
如果找到就返回Node,如果没有就返回null
* */
public HeroNode preOrderSearch(int no){
//比较当前节点是不是要查找的元素
System.out.println("前序遍历查找");//可以通过该语句判断查找的次数,对于不同顺序的查找法,该语句位置不同,应该放在根节点判断上一句
if(this.no==no){
return this;
}
//判断当前节点的左子树是否为空,如果不为空,执行前序查找
// 2如果左递归前序查找,找到结点,就返回
//创建一个节点接收查找到的结点
HeroNode resNode = null;
if(this.left!=null){
resNode=this.left.preOrderSearch(no);
}
if(resNode !=null){
//说明左子树找到
return resNode;
}
//如果左递归前序查找没找到,就判断当前节点的
//右节点是否为空,如果不空,则向右递归前序查找
if(this.right!=null){
resNode= this.right.preOrderSearch(no);
}
return resNode;
}
//中序查找遍历
public HeroNode midOrderSearch(int no){
HeroNode resNode = null;
if(this.left!=null){
resNode=this.left.midOrderSearch(no);
}
//如果左节点的递归中序遍历没有找到
if(resNode !=null){
return resNode;
}
System.out.println("进入中序1查找");
if(this.no==no){
return this;
}
if(this.right!=null){
resNode=this.right.midOrderSearch(no);
}
return resNode;
}
//后序查找
public HeroNode postOrderSearch(int no){
HeroNode resNode = null;
if(this.left!=null){
resNode=this.left.postOrderSearch(no);
}
if (resNode!=null){
return resNode;
}
if(this.right!=null){
resNode=this.right.postOrderSearch(no);
}
if(resNode!=null){
return resNode;
}
//如果左右子树都没有
System.out.println("进入后序查找");
if(this.no==no){
return this;
}
return resNode;
}
}
//定义BinaryTree 二叉树
class BinaryTree{
private HeroNode root;
//设置根节点,通过根节点调用节点的方法可以增加节点
public void setRoot(HeroNode root) {
this.root = root;
}
//树的前序遍历
public void preOrder(){
if(this.root!=null){
this.root.preOrder();
}else{
System.out.println("二叉树为空无法遍历");
}
}
//树的中序遍历
public void midOrder(){
if(this.root!=null){
this.root.midOrder();
}else {
System.out.println("二叉树为空无法遍历");
}
}
//后序遍历
public void postOrder(){
if(this.root!=null){
this.root.postOrder();
}else {
System.out.println("二叉树为空无法遍历");
}
}
//前序查找
public HeroNode preOrderSearch(int no){
if(root!=null){
return root.preOrderSearch(no);
}else {
return null;
}
}
//中序查找
public HeroNode midOrderSearch(int no){
if(root!=null){
return root.midOrderSearch(no);
}else {
return null;
}
}
//后序查找
public HeroNode postOrderSearch(int no){
if(root!=null){
return root.postOrderSearch(no);
}else {
return null;
}
}
}
二叉树删除节点
//二叉树删除指定元素
package 算法;
public class BinaryTreeDemo {
public static void main(String[] args) {
//测试,先创建一颗二叉树
BinaryTree binaryTree = new BinaryTree();
//创建需要的节点
HeroNode node1 =new HeroNode(1,"jack");
HeroNode node2 =new HeroNode(2,"lucy");
HeroNode node3 =new HeroNode(3,"tom");
HeroNode node4 =new HeroNode(4,"gogo");
HeroNode node5 =new HeroNode(5,"dd");
//说明,先手动创建二叉树,以后用递归的方法创建二叉树
binaryTree.setRoot(node1);
node1.setLeft(node2);
node1.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
//测试删除
System.out.println("删除前的前序遍历");
binaryTree.preOrder();
System.out.println("删除后的前序遍历");
binaryTree.delNode(3);
binaryTree.preOrder();
}
}
//先创建 HeroNode节点
class HeroNode{
private int no;
private String name;
private HeroNode left;
private HeroNode right;
public HeroNode(int no,String name){
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
//重写toString()方法,用于输出
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
//编写前序遍历的方法
public void preOrder(){
System.out.println(this);//先输出父节点
//递归向左子树递归前序遍历
if(this.left !=null){
this.left.preOrder();
}
//递归向右子树前序遍历
if(this.right!=null){
this.right.preOrder();
}
}
//编写中序遍历
public void midOrder(){
//递归向左子树中序遍历
if(this.left!=null){
this.left.midOrder();
}
//输出父节点
System.out.println(this);
//递归向右子树中序遍历
if(this.right!=null){
this.right.midOrder();
}
}
// 编写后序遍历
public void postOrder(){
if (this.left!=null){
this.left.postOrder();
}
if(this.right!=null){
this.right.postOrder();
}
System.out.println(this);
}
。9k
//删除指定节点
//如果要删除的节点是叶子结点,就直接删除
//如果要删除的节点是非叶子节点,就删除该子树
//这里只进行下面5点,提前操作放在二叉树执行
/*
* 预处理:考虑如果删除后是空树,则等价将二叉树置空
*
* //然后进行下面步骤
* 1:因为二叉树是单向的,所以我们是判断当前节点的子节点是否为要删除的节点
* 而不能去判断当前结点是不是要删除的结点(this=null会有编译错误)
* 2:如果当前结点的子节点不为空,并且左子节点是要删除的结点,就将this.left=null,
* 并且就返回(结束递归删除)
* 3如果当前结点的子节点不为空,并且右子节点是要删除的结点,就将this.right=null,
* 并且就返回(结束递归删除)
* 4如果第2步和第三步没有删除结点,需要向左子树递归删除
* 5如果第4步没有删除结点,则应该向右子树删除结点
* */
public void delNode(int no){
//1先要判断左子节点是否为空,防止空指针异常,在判断左子节点是否为要删除的元素
if(this.left!=null&&this.left.no==no){
this.left=null;
return;
}
//2判断右子子节点是否为空。在判断右子节点是否为要删除的节点
if(this.right!=null&&this.right.no==no){
this.right=null;
return;
}
//3如果上面两步都没有成功删除指定的节点,向左子树进行递归删除
if(this.left!=null){
this.left.delNode(no);
}
//如果第三步也没有删除指定的元素,那就进行右子树递归删除
if(this.right!=null){
this.right.delNode(no);
}
}
}
//定义BinaryTree 二叉树
class BinaryTree{
private HeroNode root;
//设置根节点,通过根节点调用节点的方法可以增加节点
public void setRoot(HeroNode root) {
this.root = root;
}
//删除节点
public void delNode(int no){
//如果root是要删除的节点
if(root!=null) {
//判断根节点是否为空
if (root.getNo() == no) {
root = null;
} else {
root.delNode(no);
}
}else {
System.out.println("空树,不能删除");
}
}
}
顺序存储二叉树 数组和二叉树的相互转换
顺序二叉树的特点
n是从0开始的,表示二叉树中的第几个元素,而且在做第四点运算时去掉小数部分取整
用数组实现顺序存储二叉树,二叉树以不同遍历方式进行遍历,前序遍历的结果是 1 2 4 5 3 6 7
package 算法;
public class ArrBinaryTreeDemo {
public static void main(String[] args) {
int[]arr = {1,2,3,4,5,6,7};
ArrBinaryTree arrTree = new ArrBinaryTree(arr);
//前序遍历
arrTree.preOrder();
System.out.println("中序遍历");
arrTree.midOrder();
System.out.println("后序遍历");
arrTree.postOrder();
}
}
class ArrBinaryTree{
private int []arr; //存储数据的数组
public ArrBinaryTree(int[] arr) {
this.arr = arr;
}
//方法重载
public void preOrder(){
this.preOrder(0);
}
//编写一个顺序存储二叉树的前序遍历 index 是数组的下标
public void preOrder(int index){
//如果数组为空,或者arr.length=0(数组为空是指向空,长度为0是数组没有数据)
if(arr== null ||arr.length==0){
System.out.println("数组为空,不能按照二叉树的前序遍历");
}
//输出当前的这个元素
System.out.println(arr[index]);
//向左递归遍历
//判断左子节点的索引是否大于数组长度
if(index*2+1<arr.length){
preOrder(2*index+1);
}
//向右递归遍历
if(index*2+2<arr.length){
preOrder(2*index+2);
}
}
//中序遍历
public void midOrder(){
this.midOrder(0);
}
public void midOrder(int index){
if(arr==null||arr.length==0){
System.out.println("该数组为空,无法进行二叉树的中序遍历");
}
if(index*2+1<arr.length){
midOrder(2*index+1);
}
System.out.println(arr[index]);
if(index*2+2<arr.length){
midOrder(index*2+2);
}
}
//后序遍历
public void postOrder(){
this.postOrder(0);
}
public void postOrder(int index){
if(arr==null||arr.length==0){
System.out.println("数组为空");
}
if(index*2+1<arr.length){
postOrder(index*2+1);
}
if(index*2+2<arr.length){
postOrder(index*2+2);
}
System.out.println(arr[index]);
}
}
//线索化二叉树 充分利用空指针域
中序遍历线索化二叉树
package 算法.线索化二叉树;
public class BinaryTreeDemo2 {
public static void main(String[] args) {
//测试
HeroNode node1 = new HeroNode(1,"tom");
HeroNode node2 = new HeroNode(3,"jack");
HeroNode node3 = new HeroNode(6,"smith");
HeroNode node4 = new HeroNode(8,"ddd");
HeroNode node5 = new HeroNode(10,"gogo");
HeroNode node6 = new HeroNode(14,"old");
//手动创建线索二叉树
BinaryTree tree = new BinaryTree();
tree.setRoot(node1);
node1.setLeft(node2);
node1.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
tree.threadedNode();
//以10号为例,看前驱结点是否为3
HeroNode leftNoed = node5.getLeft();
System.out.println(leftNoed);
//后继结点
HeroNode rightNode = node5.getRight();
System.out.println(rightNode);
tree.threadList();
}
}
//对于线索化二叉树,应该新增属性来区分左右指针指向的类型,是子节点还是前驱后继节点
class HeroNode {
private int no;
private String name;
private HeroNode left;
private HeroNode right;
//如果 leftType为0,则指向左子节点 如果为1 则指向左前驱节点
//如果 rightType为0 ,则指向右子节点,如果为1,则指向右后继节点
private int leftType;
private int rightType;
public int getLeftType() {
return leftType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public int getRightType() {
return rightType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
//重写toString()方法,用于输出
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
//定义BinaryTree 中序线索化二叉树
class BinaryTree{
private HeroNode root;
//为了实现线索化二叉树,需要创建一个指向当前节点的前驱节点的引用,才可以完成使当前节点指向前驱节点的操作
private HeroNode pre =null;
//设置根节点,通过根节点调用节点的方法可以增加节点
public void setRoot(HeroNode root) {
this.root = root;
}
//重载
public void threadedNode(){
this.threadedNode(root);
}
//传入参数是需要线索化的节点
public void threadedNode(HeroNode node){
//如果node==null,不能线索化
if(node==null){
return;
}
//中序线索化
//1线索化左子树
threadedNode(node.getLeft());
//2线索化当前节点
/* 2.1 先处理当前节点的前驱结点
2.2 处理后继结点
2.3每处理一个结点,当当前结点时下一个结点的前驱端点
* */
//2.1
if(node.getLeft()==null){
//让当前结点的左指针指向前驱结点
node.setLeft(pre);
//修改当前结点的左指针类型
node.setLeftType(1);
}
//2.2
//对于第一个结点,没有前驱结点,它的前驱结点为空
if(pre!=null&&pre.getRight()==null){
//前驱结点的右指针指向当前结点
pre.setRight(node);
pre.setRightType(1);
}
//2.3
pre=node;
//3线索化右子树
threadedNode(node.getRight());
}
//定义遍历线索化二叉树的方法
public void threadList(){
//定义一个node,存储当前的结点,从root开始
HeroNode node = root;
while (node!=null){
//循环的找到找到leftType==0的结点,第一个找到的就是8结点
//后面随着遍历而变化,因为当lifeType等于1时,说明该节点是按照线索化
//处理后的有效结点
//先找到第一个点
while (node.getLeftType()==0){
node=node.getLeft();
}
//打印当前这个结点
System.out.println(node);
//如果当前结点的右指针指向的是后继结点,就一直输出
while (node.getRightType()==1){
node=node.getRight();
System.out.println(node);
}
//替换这个遍历结点。进行到这一步,右指针指向的一定是下一个结点,除了最后一个结点
//其他的右指针都是非空的
node=node.getRight();
}
}
}
赫夫曼树
利用数组创建赫夫曼树
package 算法.树;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HuffmanTree {
public static void main(String[] args) {
int [] arr={13,7,8,3,29,6,1};
preOrder(createHuffmanTree(arr));
}
//参数 需要创建赫夫曼树的数组,返会赫夫曼树的根节点
public static Node createHuffmanTree(int[] arr){
List<Node> list = new ArrayList<>();
//把结点存入list,并用colletions,sort根据权排序
for (int i = 0; i <arr.length ; i++) {
list.add(new Node(arr[i]));
}
//排序
Collections.sort(list);
//循环处理
//最后list中只剩下赫夫曼树的头节点
while (list.size()>1) {
//取出节点数权最少的两个二叉树
Node leftNode = list.get(0);
Node rightNode = list.get(1);
//构建一颗新的二叉树,根节点权值是两颗二叉树的权值之和
Node parent = new Node(leftNode.value + rightNode.value);
parent.left = leftNode;
parent.right = rightNode;
//从Array中剔除处理过的两颗二叉树
list.remove(leftNode);
list.remove(rightNode);
list.add(parent);
Collections.sort(list);
}
//最终只用返回赫夫曼树的头节点,就成功创建一颗赫夫曼树了
return list.get(0);
}
//编写一个调用结点前序遍历的方法
public static void preOrder(Node root){
if(root==null){
System.out.println("赫夫曼树为空");
}else {
root.preorder();
}
}
}
//结点类
//为了让Node支持Collection排序,应该实现Comparable接口
class Node implements Comparable<Node>{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
@Override
public int compareTo(Node o) {
//从小到大进行排序
return this.value-o.value;
}
//写一个前序遍历
public void preorder(){
System.out.println(this);
if(this.left!=null){
this.left.preorder();
}
if(this.right!=null){
this.right.preorder();
}
}
}
二叉排序树
二叉排序树
package 算法.树;
public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr={7,3,10,12,5,1,9};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i = 0; i <arr.length ; i++) {
binarySortTree.add(new SortTreeNode(arr[i]));
}
binarySortTree.midOrder();
}
}
//创建二叉排序树
class BinarySortTree{
private SortTreeNode root;
//添加结点的方法
public void add(SortTreeNode node){
if (root==null){
root=node;
}else {
root.add(node);
}
}
//中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
public void midOrder(){
if(root!=null){
root.midOrder();
}else {
System.out.println("排序树为空");
}
}
}
class SortTreeNode{
int value;
SortTreeNode left;
SortTreeNode right;
public SortTreeNode(int value) {
this.value = value;
}
//添加结点的方法,要满足二叉排序树的要求
public void add (SortTreeNode node){
if (node==null){
return;
}
//判断传入的结点的值和根节点的关系
if (node.value<this.value){
if (this.left==null){
this.left=node;
}else {//向左子树递归
this.left.add(node);
}
}else {//添加结点的值大于等于当前结点的值
if(this.right==null){
this.right=node;
}else {
this.right.add(node);
}
}
}
//中序遍历
public void midOrder(){
if (this.left!=null){
this.left.midOrder();
}
System.out.println(this);
if (this.right!=null){
this.right.midOrder();
}
}
@Override
public String toString() {
return "SortTreeNode{" +
"value=" + value +
'}';
}
}
删除二叉排序树的结点
删除一个子节点的时候还要考虑删除结点是根节点时候的情况(没有父节点)
package 算法.树;
public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i = 0; i < arr.length; i++) {
binarySortTree.add(new SortTreeNode(arr[i]));
}
binarySortTree.midOrder();
binarySortTree.delNode(2);
binarySortTree.delNode(5);
binarySortTree.delNode(9);
binarySortTree.delNode(12);
binarySortTree.delNode(7);
binarySortTree.delNode(3);
binarySortTree.delNode(10);
binarySortTree.delNode(1);
System.out.println("");
binarySortTree.midOrder();
System.out.println(binarySortTree.getRoot());
}
}
//创建二叉排序树
class BinarySortTree {
private SortTreeNode root;
//添加结点的方法
public void add(SortTreeNode node) {
if (root == null) {
root = node;
} else {
root.add(node);
}
}
public SortTreeNode getRoot() {
return root;
}
//查找要删除的结点
public SortTreeNode search(int value) {
if (root == null) {
return null;
} else {
return root.search(value);
}
}
//查找要删除结点的父节点
public SortTreeNode searchParent(int value) {
if (root == null) {
return null;
} else {
return root.searchParent(value);
}
}
//删除结点
public void delNode(int value) {
if (root == null) {
return;
} else {
//找到要删除的结点
SortTreeNode targetNode = root.search(value);
if (targetNode == null) {
//没找到
return;
}
//如果找到,但是根节点的左和右都为空,证明根节点就是要找的点,且只有一个根节点
if (root.left == null && root.right == null) {
root = null;
return;
}
//根据三种情况开始判断。
SortTreeNode parentNode = root.searchParent(value);
//第一种情况如果要删除的结点是叶子结点
if (targetNode.left == null && targetNode.right == null) {
//判断删除结点是父节点的左节点还是右结点
if (parentNode.left != null && parentNode.left.value == value) {
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.value == value) {
parentNode.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) {
//先判断targetNode有两个子结点的情况,因为只有一个子节点的判断过于繁琐
SortTreeNode temp = targetNode.right;
while (temp.left != null) {
temp = temp.left;//循环找到targetNode右子树的最小值
}
//删除targetNode右子树最小的结点(最小的结点要么只有右子树,要么没有子结点,符合另外两种情况)
delNode(temp.value);
//把temp中的数据赋给target
targetNode.value = temp.value;
} else {
//删除的结点只有一个结点
if (targetNode.left != null) {//删除结点的左子节点不空
//要考虑当targetNOde是根节点,没有父节点,且它只有一个子节点
if (parentNode != null) {//父节点不为空
if (parentNode.left != null && parentNode.left.value == targetNode.value) {
parentNode.left = targetNode.left;
} else {
parentNode.right = targetNode.left;
}
} else {
root = targetNode.left;
}
} else {//targetNode的右子节点不为空
if (parentNode != null) {
if (parentNode.left != null && parentNode.left.value == targetNode.value) {
parentNode.left = targetNode.right;
} else {
parentNode.right = targetNode.right;
}
} else {
root=targetNode.right;
}
}
}
}
}
//中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
public void midOrder() {
if (root != null) {
root.midOrder();
} else {
System.out.println("排序树为空");
}
}
}
class SortTreeNode {
int value;
SortTreeNode left;
SortTreeNode right;
public SortTreeNode(int value) {
this.value = value;
}
//查找要删除结点的方法
public SortTreeNode search(int value) {
if (this.value == value) {
return this;
} else if (value < this.value) {
if (this.left == null) {
return null;
}
return this.left.search(value);
} else {//要查找的值不小于当前结点的value
if (this.right == null) {
return null;
}
return this.right.search(value);
}
}
//查找要删除结点的父节点
public SortTreeNode searchParent(int value) {
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
return this;
} else {
if (this.left != null && value < this.value) {
return this.left.searchParent(value);
} else if (this.right != null && value >= this.value) {
return this.right.searchParent(value);
} else {
return null;//没有父节点
}
}
}
//添加结点的方法,要满足二叉排序树的要求
public void add(SortTreeNode node) {
if (node == null) {
return;
}
//判断传入的结点的值和根节点的关系
if (node.value < this.value) {
if (this.left == null) {
this.left = node;
} else {//向左子树递归
this.left.add(node);
}
} else {//添加结点的值大于等于当前结点的值
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
}
}
//中序遍历
public void midOrder() {
if (this.left != null) {
this.left.midOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.midOrder();
}
}
@Override
public String toString() {
return "SortTreeNode{" +
"value=" + value +
'}';
}
}
平衡二叉树
package 算法.树.平衡二叉树;
public class AVLTreeDemo {
public static void main(String[] args) {
//int[] arr = {4,3,6,5,7,8};
int[] arr = {10,12,8,9,7,6};
AVLTree avlTree= new AVLTree();
for (int i = 0; i <arr.length ; i++) {
avlTree.add(new AVLTreeNode(arr[i]));
}
avlTree.midOrder();
System.out.println(avlTree.getRoot().height());
System.out.println(avlTree.getRoot().leftHeight());
System.out.println(avlTree.getRoot().rightHeight());
}
}
//创建AVLTree
class AVLTree {
private AVLTreeNode root;
//左旋方法
//增加查找当前结点子树高度的方法
//添加结点的方法
public void add(AVLTreeNode node) {
if (root == null) {
root = node;
} else {
root.add(node);
}
}
public AVLTreeNode getRoot() {
return root;
}
//查找要删除的结点
public AVLTreeNode search(int value) {
if (root == null) {
return null;
} else {
return root.search(value);
}
}
//查找要删除结点的父节点
public AVLTreeNode searchParent(int value) {
if (root == null) {
return null;
} else {
return root.searchParent(value);
}
}
//删除结点
public void delNode(int value) {
if (root == null) {
return;
} else {
//找到要删除的结点
AVLTreeNode targetNode = root.search(value);
if (targetNode == null) {
//没找到
return;
}
//如果找到,但是根节点的左和右都为空,证明根节点就是要找的点,且只有一个根节点
if (root.left == null && root.right == null) {
root = null;
return;
}
//根据三种情况开始判断。
AVLTreeNode parentNode = root.searchParent(value);
//第一种情况如果要删除的结点是叶子结点
if (targetNode.left == null && targetNode.right == null) {
//判断删除结点是父节点的左节点还是右结点
if (parentNode.left != null && parentNode.left.value == value) {
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.value == value) {
parentNode.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) {
//先判断targetNode有两个子结点的情况,因为只有一个子节点的判断过于繁琐
AVLTreeNode temp = targetNode.right;
while (temp.left != null) {
temp = temp.left;//循环找到targetNode右子树的最小值
}
//删除targetNode右子树最小的结点(最小的结点要么只有右子树,要么没有子结点,符合另外两种情况)
delNode(temp.value);
//把temp中的数据赋给target
targetNode.value = temp.value;
} else {
//删除的结点只有一个结点
if (targetNode.left != null) {//删除结点的左子节点不空
//要考虑当targetNOde是根节点,没有父节点,且它只有一个子节点
if (parentNode != null) {//父节点不为空
if (parentNode.left != null && parentNode.left.value == targetNode.value) {
parentNode.left = targetNode.left;
} else {
parentNode.right = targetNode.left;
}
} else {
root = targetNode.left;
}
} else {//targetNode的右子节点不为空
if (parentNode != null) {
if (parentNode.left != null && parentNode.left.value == targetNode.value) {
parentNode.left = targetNode.right;
} else {
parentNode.right = targetNode.right;
}
} else {
root = targetNode.right;
}
}
}
}
}
//中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
public void midOrder() {
if (root != null) {
root.midOrder();
} else {
System.out.println("排序树为空");
}
}
}
class AVLTreeNode {
int value;
AVLTreeNode left;
AVLTreeNode right;
public AVLTreeNode(int value) {
this.value = value;
}
//右旋的方法
private void rightRotate(){
AVLTreeNode newNode = new AVLTreeNode(value);
newNode.right=right;
newNode.left=left.right;
value=left.value;
left=left.left;
right=newNode;
}
//左旋的方法
private void leftRotate(){
//根据当前根节点的值创建一个新的结点
AVLTreeNode newNode= new AVLTreeNode(value);
//新结点的左子树指向根节点的左子树
newNode.left=left;
//新结点的右子树指向根节点的右子树的左子树
newNode.right= this.right.left;
//把当前根节点的值替换成它右子树的值
this.value=right.value;
//当前结点的右子树设置成右子树的右子树
this.right=right.right;
//当前结点的左子树指向新结点
left= newNode;
}
//返回左子树高度
public int leftHeight() {
if (left == null) {
return 0;
} else {
return left.height();
}
}
//返回右子树高度
public int rightHeight() {
if (right == null) {
return 0;
} else {
return right.height();
}
}
//返回以当前结点为根节点的树高度,因为要算上当前节点,所以要加一
public int height() {
return Math.max(left == null ? 0 : left.height() , right == null ? 0 : right.height())+1;
}
//查找要删除结点的方法
public AVLTreeNode search(int value) {
if (this.value == value) {
return this;
} else if (value < this.value) {
if (this.left == null) {
return null;
}
return this.left.search(value);
} else {//要查找的值不小于当前结点的value
if (this.right == null) {
return null;
}
return this.right.search(value);
}
}
//查找要删除结点的父节点
public AVLTreeNode searchParent(int value) {
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
return this;
} else {
if (this.left != null && value < this.value) {
return this.left.searchParent(value);
} else if (this.right != null && value >= this.value) {
return this.right.searchParent(value);
} else {
return null;//没有父节点
}
}
}
//添加结点的方法,要满足二叉排序树的要求
public void add(AVLTreeNode node) {
if (node == null) {
return;
}
//判断传入的结点的值和根节点的关系
if (node.value < this.value) {
if (this.left == null) {
this.left = node;
} else {//向左子树递归
this.left.add(node);
}
} else {//添加结点的值大于等于当前结点的值
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
}
//当添加一个结点后,右子树的高度-左子树的高度>1,进行左旋
if (rightHeight()-leftHeight()>1){
//如果根节点的右子节点的左子树高度大于右子树
if(right.leftHeight()>rightHeight()){
right.rightRotate();
}
this.leftHeight();
return;//如果进行了一次左旋,就不进行右旋的判断了
}
if(leftHeight()-rightHeight()>1){
//如果根节点的左子结点的右子树高度大于左子树
if (left.rightHeight()>leftHeight()){
//左子结点进行一次左旋
left.leftRotate();
}
this.rightRotate();
}
}
//中序遍历
public void midOrder() {
if (this.left != null) {
this.left.midOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.midOrder();
}
}
@Override
public String toString() {
return "AVLTreeNode{" +
"value=" + value +
'}';
}
}
多叉树(B树)
二叉树的问题分析
图
邻接表
package 算法.图;
import java.util.ArrayList;
import java.util.Arrays;
public class Graph {
private ArrayList<String> vertexList;//存储顶点集合
private int [][] edges;//存储图的邻接矩阵
private int numOfEdges; //边的数目Arr
//构造器 参数的顶点个数
public Graph(int n){
edges=new int[n][n];
vertexList=new ArrayList<String>();
numOfEdges=0;
}
//插入顶点
public void insertVertex(String vertex){
vertexList.add(vertex);
}
//添加边
/*v1 v2 是顶点的下标 比如第一个顶点A下标是0 第二个i当地B是1
* weight 是用于修改矩阵的值
* */
public void insertEdge(int v1,int v2,int weight){
edges[v1][v2] =weight;
edges[v2][v1]=weight;
numOfEdges++;
}
//图中常用方法
//返回结点个数
public int getVertexNum(){
return vertexList.size();
}
//边的数组
public int gerEdges(){
return numOfEdges;
}
//返回结点i(下标)对应的数据 0->A
public String getValueByIndex(int i){
return vertexList.get(i);
}
//返回v1和v2的权值
public int getWeight(int v1,int v2){
return edges[v1][v2];
}
//显示矩阵
public void showGraph(){
for (int []link:edges){
System.out.println(Arrays.toString(link));
}
}
public static void main(String[] args) {
int n=5;
String [] vertexList={"a","b","c","d","f"};
Graph graph = new Graph(n);
for (int i = 0; i <vertexList.length ; i++) {
graph.insertVertex(vertexList[i]);
}
//添加边
//AB AC BC BD BE
graph.insertEdge(0,1,1);
graph.insertEdge(0,2,1);
graph.insertEdge(1,2,1);
graph.insertEdge(1,3,1);
graph.insertEdge(1,4,1);
graph.showGraph();
}
}
图的深度优先dps
package 算法.图;
import java.util.ArrayList;
import java.util.Arrays;
public class Graph {
private ArrayList<String> vertexList;//存储顶点集合
private int [][] edges;//存储图的邻接矩阵
private int numOfEdges; //边的数目Arr
//构造器 参数的顶点个数
private boolean[] isVisted;//记录某个结点是否被访问
public Graph(int n){
edges=new int[n][n];
vertexList=new ArrayList<String>();
numOfEdges=0;
isVisted = new boolean[n];
}
//深度优先算法//第一次下标是0
public void dfs(boolean[] isVisted,int i){
//首先访问一个顶点时输出
System.out.print(getValueByIndex(i)+"->");
//把访问过的结点设置为已经访问
isVisted[i]=true;
//查找结点i的第一个邻接点
int w = getFirstNeighbor(i);
while (w!=-1){//如果有邻接点
if (!isVisted[w]){//没有被访问过
//以当前结点w递归dfs
dfs(isVisted,w);
}
//如果已经访问过,返回i,访问下一个邻接结点
w=getNextNeighbor(i,w);
}
}
//对dfs 进行一个重载,遍历所有结点进行dfs
public void dfs(){
//遍历所有的结点,进行dfs【回溯】
for (int i = 0; i <getVertexNum() ; i++) {
if (!isVisted[i]){
dfs(isVisted,i);
}
}
}
//根据前一个邻接结点的下标(v2)来获取下一个邻接结点的下标
public int getNextNeighbor(int v1,int v2){
for (int i = v2+1; i <vertexList.size() ; i++) {
if(edges[v1][i]>0){
return i;
}
}
return -1;
}
//得到当前结点的第一个邻接结点的下标
/*index 当前结点的下标
* 如果存在就返回邻接结点下标 否则返回-1
* */
public int getFirstNeighbor(int index){
for (int i = 0; i <vertexList.size() ; i++) {
if (edges[index][i]>0){
return i;
}
}
return -1;
}
//插入顶点
public void insertVertex(String vertex){
vertexList.add(vertex);
}
//添加边
/*v1 v2 是顶点的下标 比如第一个顶点A下标是0 第二个i当地B是1
* weight 是用于修改矩阵的值
* */
public void insertEdge(int v1,int v2,int weight){
edges[v1][v2] =weight;
edges[v2][v1]=weight;
numOfEdges++;
}
//图中常用方法
//返回结点个数
public int getVertexNum(){
return vertexList.size();
}
//边的数组
public int gerEdges(){
return numOfEdges;
}
//返回结点i(下标)对应的数据 0->A
public String getValueByIndex(int i){
return vertexList.get(i);
}
//返回v1和v2的权值
public int getWeight(int v1,int v2){
return edges[v1][v2];
}
//显示矩阵
public void showGraph(){
for (int []link:edges){
System.out.println(Arrays.toString(link));
}
}
public static void main(String[] args) {
int n=5;
String [] vertexList={"a","b","c","d","e"};
Graph graph = new Graph(n);
for (int i = 0; i <vertexList.length ; i++) {
graph.insertVertex(vertexList[i]);
}
//添加边
//AB AC BC BD BE
graph.insertEdge(0,1,1);
graph.insertEdge(0,2,1);
graph.insertEdge(1,2,1);
graph.insertEdge(1,3,1);
graph.insertEdge(1,4,1);
graph.showGraph();
System.out.println("深度遍历");
graph.dfs();
}
}
变长编码 需要满足字符编码不能是其他编码的前缀
赫夫曼编码
赫夫曼编码就是满足了前缀要求的边长编码
package 算法.赫夫曼编码的实现;
//数据压缩的实现
import java.util.*;
public class HuffmanCode {
public static void main(String[] args) {
String content = "java是世界上最好的语言!";
byte[] contentBytes = content.getBytes();
byte[] huffmanCodeBytes = huffmanZip(contentBytes);
System.out.println(Arrays.toString(huffmanCodeBytes));
//解压获取到原来的字节数组
byte [] decodeByte= decode(huffmanCodes,huffmanCodeBytes);
System.out.println(new String(decodeByte));
}
//接收一个字符数组,返回一个建立好的list<Node>
public static List<Node> getNodes(byte[] bytes) {
//创建一个arrayList
List<Node> list = new ArrayList<>();
//创建一个hashMap存放字符和字符出现的次数 key是字符 value是出现的次数
Map<Byte, Integer> counts = new HashMap<>();
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
//把map中的键值对转成node 加入list
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
list.add(new Node(entry.getKey(), entry.getValue()));
}
return list;
}
//参数 需要创建赫夫曼树的list,返回赫夫曼树的根节点
public static Node createHuffmanTree(List<Node> nodes) {
//把list排序
Collections.sort(nodes);
//循环处理
//最后list中只剩下赫夫曼树的头节点
while (nodes.size() > 1) {
//取出节点数权最少的两个二叉树
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
//构建一颗新的二叉树,根节点权值是两颗二叉树的权值之和,没有Byte域
Node parent = new Node(null, leftNode.weight + rightNode.weight);
parent.left = leftNode;
parent.right = rightNode;
//从Array中剔除处理过的两颗二叉树
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
Collections.sort(nodes);
}
//最终只用返回赫夫曼树的头节点,就成功创建一颗赫夫曼树了
return nodes.get(0);
}
//编写一个调用结点前序遍历的方法
public static void preOrder(Node root) {
if (root == null) {
System.out.println("赫夫曼树为空");
} else {
root.preorder();
}
}
//生成赫夫曼编码表,存放在map<Byte,String>
static Map<Byte, String> huffmanCodes = new HashMap<>();
//在生成赫夫曼编码时,需要用字符串拼接
static StringBuilder stringBuilder = new StringBuilder();
/*
* 功能,将传入的node结点的所有叶子结点的赫夫曼编码得到,放入map
* node 传入结点
* code 路径 左子节点是0 右子节点是1
* StringBuilder 字符串拼接
* */
private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
//把形式参数中的内容传给新的stringBuilder
StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
stringBuilder1.append(code);
if (node != null) {//node等于null 不处理
//判断当前结点是叶子结点还是非叶子结点
if (node.data == null) {
//非叶子结点向左递归
getCodes(node.left, "0", stringBuilder1);
//向右递归
getCodes(node.right, "1", stringBuilder1);
} else {//如果是叶子结点
huffmanCodes.put(node.data, stringBuilder1.toString());
}
}
}
//为了调用方便重载getCode,只需要传入赫夫曼树的根节点
private static Map<Byte, String> getCodes(Node root) {
if (root == null) {
return null;
}
getCodes(root, "", stringBuilder);
return huffmanCodes;
}
//将字符串对应的byte[] 通过赫夫曼编码表压缩
//返回的是赫夫曼编码处理后的byte
//也就是先转成对应的赫夫曼编码的字符串,再8位数一组转换成的byte,
//并且由于计算机用二进制补码存储数据,输出的时候我们可以把得到的byte转换成我们的十进制数
public static int lastStrLen=0;//定义一个全局变量,保存最后字节数组最后一个元素转成字符串的位数
private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
//利用stringBuilder将bytes转成赫夫曼编码对应的字符串
StringBuilder stringBuilder = new StringBuilder();
for (byte b : bytes) {
stringBuilder.append(huffmanCodes.get(b));
}
System.out.println("测试"+stringBuilder);
//将"1010100001111"对应的字符串8位一组转换成byte[]
//len 是字节数
int len;
if (stringBuilder.length() % 8 == 0) {
len = stringBuilder.length() / 8;
} else {
len = stringBuilder.length() / 8 + 1;
}
//存储赫夫曼编码压缩后的byte数组
byte[] HuffmanCodeBytes = new byte[len];
int index = 0;
for (int i = 0; i < stringBuilder.length(); i += 8) {
String str;
if (i + 8 > stringBuilder.length()) {
//如果剩下位数不够8位
str = stringBuilder.substring(i);//把剩下的位数全部加入str
lastStrLen=str.length();
} else {
str = stringBuilder.substring(i, i + 8);
//将str转成byte,放入resByte
}
HuffmanCodeBytes[index] = (byte)Integer.parseInt(str,2);
//使用下面的方法,当8位数的最高位是1时,会发生越界,原因是parseByte会把传入的字符串转换成int,转化后
//由于int有32位,也就是最高位不为0,导致转换后的数字超过128
//HuffmanCodeBytes[index] = Byte.parseByte(str,2);
index++;
}
return HuffmanCodeBytes;
}
//传入原字节数组,返回压缩后的数组
//使用一个方法将前面的方法封装起来,便于调用
private static byte[] huffmanZip(byte[] bytes){
//把原字节数组创建成一个list<Node> node中有字符和其出现的次数
List<Node> nodes=getNodes(bytes);
//通过list创建赫夫曼树,返回赫夫曼树的根节点
Node huffmanTreeRoot=createHuffmanTree(nodes);
//根据赫夫曼树生成赫夫曼编码
Map<Byte,String> huffmanCodes = getCodes(huffmanTreeRoot);
//根据生成的赫夫曼编码得到压缩后的赫夫曼编码数组
byte[] huffmanCodeBytes =zip(bytes,huffmanCodes);
return huffmanCodeBytes;
}
/*将一个byte 转换成一个二进制的字符串
* b传入的byte
* flag 是否是字节数组最后一个字节,如果是,由于最后一位字节不一定是8位数,需要特殊处理
* 返回 b对应的二进制字符串 补码;
*
* */
private static String byteToString(boolean flag,byte b){
//将b转成成int,byte转换成二进制字符串的方法
int temp = b;
//如果要补位,因为int小于8个字节的数转换成二进制,不会显示高位
//比如 int a=1; Integer.toBinaryString(a) =1 ;低8位负数和正数和1 0000 0000按位或然后截取低8位的话都不会改变值
//这样就解决了正数补码只显示低位,和负数补码32位都显示的问题
//我们需要把1与1 0000 0000(256)按位或,补齐8位
if (flag){
temp|=256;// 0000 0001 |1 0000 0000 = 1 0000 0001(257)
}else {
temp|= (1<<lastStrLen);
}
String str = Integer.toBinaryString(temp);//把 1 0000 0001 对应的int 转换成二进制补码
if (flag){
return str.substring(str.length()-8);//截取最后8位
}else {//如果是最后一位
return str.substring(str.length()-lastStrLen);
}
}
//编写解码的方法
//传入赫夫曼编码表和经过压缩的字节数组
private static byte[] decode(Map<Byte,String>huffmanCodes,byte [] huffmanBytes){
StringBuffer stringBuffer = new StringBuffer();
//将字节数组转换成二进制字符串
for (int i = 0; i <huffmanBytes.length ; i++) {
byte b = huffmanBytes[i];
//判断是不是最后一个值
boolean flag=(i==huffmanBytes.length-1);
stringBuilder.append(byteToString(!flag,b));
}
System.out.println("测试"+stringBuilder);
//把字符串按照指定的赫夫曼编码解码
//把赫夫曼编码表进行调换。反向查询,接入到map
Map<String,Byte> map = new HashMap<>();
for (Map.Entry<Byte,String> entry:huffmanCodes.entrySet()) {
map.put(entry.getValue(),entry.getKey());
}
//创建一个集合存放byte
List<Byte> list = new ArrayList<>();
for (int i = 0; i <stringBuilder.length() ; ) {
int count =1;
boolean flag =true;
Byte b = null;
while (flag){
String key = stringBuilder.substring(i,i+count);
b = map.get(key);
if (b==null){
count++;
}else {
//匹配成功
flag=false;
}
}
list.add(b);
i +=count;
}
//当for循环结束,我们已经获得了原来数据的byte形式,我们将其放入byte[]返回
byte[] bytes=new byte[list.size()];
for (int i = 0; i <bytes.length ; i++) {
bytes[i]=list.get(i);
}
return bytes;
}
}
//结点类
//为了让Node支持Collection排序,应该实现Comparable接口
class Node implements Comparable<Node> {
//byte的包装类.是字节数据类型
Byte data;//存放数据本身的AScii编码,'a'->97
int weight;//权值,字符出现的次数
Node left;
Node right;
public Node(Byte data, int weight) {
this.weight = weight;
this.data = data;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", weight=" + weight +
'}';
}
@Override
public int compareTo(Node o) {
//从小到大进行排序
return this.weight - o.weight;
}
//写一个前序遍历,用于测试
public void preorder() {
System.out.println(this);
if (this.left != null) {
this.left.preorder();
}
if (this.right != null) {
this.right.preorder();
}
}
}
时间复杂度
排序算法
稳定性表示两个一样大小的值在排序后是否会交换原来的顺序
冒泡排序
//冒泡算法(从小到大)(可以有相等的数值) o(n^2)
package 算法;
public class MaoPaoSort {
public static void main(String[] args) {
int []array = {4,5,2,11,6,3};
for (int i = 0; i <array.length-1 ; i++) {
for (int j = 0; j < array.length-i-1; j++) {
int temp;
if(array[j]>array[j+1]){
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
for (int i = 0; i <array.length ; i++) {
System.out.println(array[i]);
}
}
}
冒泡排序优化,如果发现在某次排序中,没有进行过交换,说明已经有序,我们可以用一个变量来标识
//冒泡算法(从小到大)(可以有相等的数值),8万次20秒
//平均复杂度 o(n^2) 稳定
package 算法;
public class MaoPaoSort {
public static void main(String[] args) {
int []array = {4,5,2,11,6,3};
boolean flag =false;//标识
//计算八万个数排序时间
int[] arr = new int[80000];
for(i=0;i<80000;i++){
arr[i]= (int)(Math.random()*80000);
}
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
String dateStr1 = simpleDateFormat.format(date1);
System.out.println("排序前的时间是"+dateStr1);
for (int i = 0; i <array.length-1 ; i++) {
for (int j = 0; j < array.length-i-1; j++) {
int temp;
if(array[j]>array[j+1]){
flag=true;
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
if(!flag){//在一次排序中一次交换都没有发生
break;
}else{
flag =false;//重置flag,进行下一次交换
}
}
for (int i = 0; i <array.length ; i++) {
System.out.println(array[i]);
}
Date date2 = new Date();
String dateStr2 = simpleDateFormat.farmat(date2)
System.out.print;
}
}
选择排序
//选择排序(从小到大)(可以有相等的数值),虽然复杂度都是0(n^2),但是运行时间比冒泡快,8万次 5秒
//不稳定
package 算法;
public class SelectSort {
public static void main(String[] args) {
int []array ={7,5,9,2,3,4,4};
for (int i = 0; i < array.length-1; i++) {
int min = i;
for (int j = i; j <array.length-1 ; j++) {
if(array[min]>array[j+1]){
min=j+1;
}
}
if(i!=min){
int temp;
temp=array[i];
array[i]=array[min];
array[min]=temp;
}
}
for (int i = 0; i <array.length ; i++) {
System.out.println(array[i]);
}
}
}
插入排序
package 算法.排序算法; 8万次大概5秒 时间复杂度o(n^2)
//稳定
public class InsertSort {
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i <10; i++) {
arr[i]= (int)(Math.random()*10);
}
sort(arr);
for (int i = 0; i <arr.length ; i++) {
System.out.print(arr[i]+" ");
}
}
public static void sort(int [] arr){
//创建变量应该放在循环的外面,节省内存开销
int insertVal = 0;//定义待插入的数
int insertIndex=0;//定义插入下标
for (int i = 1; i < arr.length; i++) {
insertVal = arr[i];
insertIndex = i;
if(insertVal<arr[insertIndex-1]){
while (insertIndex-1>=0&&arr[insertIndex-1]>insertVal){
arr[insertIndex]=arr[insertIndex-1];
insertIndex--;
}
}
arr[insertIndex]=insertVal;
}
}
}
希尔排序
package 算法.排序算法;
//o(nlogn) 8万次大概1秒 不稳定
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class ShellSort {
public static void main(String[] args) {
int[] arr=new int[80000];
for (int i = 0; i <arr.length ; i++) {
arr[i]=(int)(Math.random()*80000);
}
Date date1 =new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
String dateStr1 = simpleDateFormat.format(date1);
System.out.println(dateStr1);
//shellSort1(arr);
shellSort2(arr);
Date date2 = new Date();
System.out.println(simpleDateFormat.format(date2));
}
//希尔排序交换法,效率比移动法低 8万次排序17秒
public static void shellSort1(int [] arr){
int temp = 0;
//把所有元素看出独立的组,每次循环组数除2,直到剩下一个大组
for (int gap =arr.length/2; gap >0 ; gap/=2) {
//每次循环,都把各个小组的数字排序,这里小组的排序不是插入排序,效率低
for (int i = gap; i <arr.length ; i++) {
for (int j = i-gap; j >=0 ; j-=gap) {
if(arr[j]>arr[j+gap]){
temp=arr[j];
arr[j]=arr[j+gap];
arr[j+gap]=temp;
}
}
}
}
System.out.println(Arrays.toString(arr));
}
//希尔排序移动法,效率很高,8万次大概1秒
public static void shellSort2(int[] arr){
for (int gap = arr.length/2; gap >0 ; gap/=2) {
//从第gap个元素,逐个对其所在的组进行插入排序
for (int i = gap; i <arr.length ; i++) {
int j= i;
int temp = arr[j];
//当要插入的值比前面的值大,则把插入的值放到一个合适的位置,否则当前位置就是要插入的位置,不做改动
if(arr[j-gap]>arr[j]){
while (j-gap>=0&&arr[j-gap]>temp){
arr[j]= arr[j-gap];
j -=gap;
}
//退出while循环时,当前位置就适当的插入位置
arr[j]=temp;
}
}
}
//System.out.println(Arrays.toString(arr));
}
}
快速排序
//快速排序比希尔排序更快 800万个数排序4秒左右
// nlog n 不稳定
package 算法.排序算法;
import java.util.*;
public class QuickSort {
public static void main(String[] args) {
int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };
quickSort(arr, 0, arr.length - 1);
System.out.println("排序后:");
System.out.println(Arrays.toString(arr));
}
private static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// 找寻基准数据的正确索引
int index = getIndex(arr, low, high);
// 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序
quickSort(arr, 0, index - 1);
quickSort(arr, index + 1, high);
}
}
private static int getIndex(int[] arr, int low, int high) {
// 基准数据
int tmp = arr[low];
while (low < high) {
// 当队尾的元素大于等于基准数据时,向前挪动high指针
while (low < high && arr[high] >= tmp) {
high--;
}
// 如果队尾元素小于tmp了,需要将其赋值给low
arr[low] = arr[high];
// 当队首元素小于等于tmp时,向前挪动low指针
while (low < high && arr[low] <= tmp) {
low++;
}
// 当队首元素大于tmp时,需要将其赋值给high
arr[high] = arr[low];
}
// 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
// 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low]
arr[low] = tmp;
return low; // 返回tmp的正确位置
}
}
归并排序
//800万次排序大概3秒 稳定
//nlog n
package 算法.排序算法;
import java.util.Arrays;
public class MergetSort {
public static void main(String[] args) {
int[] arr ={8,4,5,7,1,3,6,2};
int[]temp = new int[arr.length];
mergetSort(arr,0,arr.length-1,temp);
System.out.println(Arrays.toString(arr));
}
//分解和递归调用merge
public static void mergetSort(int arr[],int left,int right,int[] temp){
if(left<right){
int mid = (left+right)/2;
//向左递归进行分解
mergetSort(arr,left,mid,temp);
//向右递归
mergetSort(arr,mid+1,right,temp);
//合并
merge(arr,left,mid,right,temp);
}
}
//合并的方法
/*
* arr 排序的原始数组
* left 左边有序序列的初始索引
* mid+1 右边有序序列的初始索引
* right 右边有序序列的最后一个索引
* temp 中转数组
* */
public static void merge(int[] arr,int left,int mid ,int right,int[] temp){
int i = left; //左边序列初始索引
int j = mid+1; // 右边序列初始索引
int t = 0 ; //指向临时数组当前索引的辅助指针
//一,先把左右两边有序的数组按照规则填充到temp数组
//直到两边的有序序列有一边处理完为止
while (i<=mid&&j<=right){
//如果左边的有序序列的当前元素,小于右边有序序列的当前数据
//就把左边的数据填充到temp
if(arr[i]<=arr[j]){
temp[t]=arr[i];
i++;
t++;
}else {//如果右边小于左边
temp[t]=arr[j];
t++;
j++;
}
}
//二把有剩余数据的一边,依次把数据加入到temp
while (i<=mid){
temp[t]=arr[i];
t++;
i++;
}
while (j<=right){
temp[t]=arr[j];
t++;
j++;
}
//三 把temp的数据元素拷贝到arr中
t=0;
int tempLeft = left;
while (tempLeft<=right){
arr[tempLeft]=temp[t];
tempLeft++;
t++;
}
}
}
基数排序
//基数排序比快速排序还要快 800万大概一秒,但是耗费比较多的内存
//8000万次排序 大概需要3.3g内存
//稳定 复杂度 o(n*k) k是桶的个数
// 80000000*11(二维数组加临时数组共11个数组)*4(int字节长度)/1024/1024/1024=3.3g
package 算法.排序算法;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr ={53,3,542,748,14,214};
radoxSort(arr);
System.out.println(Arrays.toString(arr));
}
//基数排序方法
public static void radoxSort(int[] arr){
//得到数组中最大的数
//假设第一个就是最大的
int max = arr[0];
for (int i = 0; i <arr.length ; i++) {
if(arr[i]>max){
max= arr[i];
}
}
//得到最大的位数是几
int maxLength = (max+"").length();
//定义一个二维数组,表示10个桶
int[][] bucket = new int[10][10];
//为了记录每次循环,每个桶中放入了多少个数据,用一个一维数组来记录
int[] bucketElementCount=new int[10];
for (int i = 0,n=1; i <maxLength ; i++,n*=10) {
//从最低位开始,到最高位,依次进行桶排序
//取出每个元素对应的位数,第一次取个位,直到取到最高位
for (int j = 0; j <arr.length ; j++) {
int digitOfElement = arr[j]/n%10;
//根据取出的值放入对应的桶中
bucket[digitOfElement][bucketElementCount[digitOfElement]]=arr[j];
bucketElementCount[digitOfElement]++;
}
//按照桶中放入的元素顺序取出,放入原来的数组
int index =0;
for (int k = 0; k <bucketElementCount.length ; k++) {
//判断桶中是否有数据
if(bucketElementCount[k]!=0){
for (int l = 0; l <bucketElementCount[k] ; l++) {
arr[index++]=bucket[k][l];
}
}
//每一次遍历完一个桶,就把存放这个桶有效数字个数的数组的对应值清空
bucketElementCount[k]=0;
}
}
}
}
堆排序
https://www.cnblogs.com/chengxiao/p/6129630.html
package 算法.排序算法;
//堆排序 8万次大概4秒 复杂度 n*log(n);
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {4,6,8,5,9,10,10,45,45,77};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
//编写一个堆排序的方法,将数组升序排序
public static void heapSort(int[] arr){
//最后一个非叶子结点的位置
int temp = 0;
for (int i = arr.length/2-1; i >=0 ; i--) {
adjustHeap(arr,i,arr.length);
}
//循环过后就可以得到一个大顶堆,
for (int j = arr.length-1; j >0 ; j--) {
temp=arr[j];
arr[j]=arr[0];
arr[0]=temp;
//经过第一次顶堆排序,剩下的结点具备了大顶堆的特点,只需要判断第一个根节点的左右子节点大小就可以了。
adjustHeap(arr,0,j);
}
}
//将一个数组(二叉树),调整成一个大顶堆
/*
arr 要调整的数组
i 表示最后一个还没处理的非叶子结点在数组中的索引
length 表示多少个元素继续调整,length是不断减少的
*/
public static void adjustHeap(int []arr,int i,int length){
int temp = arr[i];
for (int k = i*2+1; k <length ; k=k*2+1) {
if (k+1<length&&arr[k]<arr[k+1]){
//如果有子节点大于左字节点
k++;
}
if (arr[k]>temp){
arr[i]=arr[k];
i=k;//i的位置插入了arr[k]的值,此时应该把temp的值寻找一个合适的位置放置(在arr[k]的子节点)
}else {
break;
}
}//放for循环结束,我们已经把以i为父节点的树的最大值放在了arr[i]的位置
//此时应该把temp放到找到的合适位置
arr[i]=temp;
}
}
查找算法
//二分法
//二分法查找 :查找指定值的元素
// 该方法建立在数组已经排序的基础上
// 下面程序基于从小到大排序分析
//且数组中没有重复的元素
//二分法查找 :查找指定值的元素
// 该方法建立在数组已经排序的基础上
// 下面程序基于从小到大排序分析
//且数组中没有重复的元素
public class Test{
public static void main(String[] args){
int[] a = {1,2,3,4,7,9,45,75,100};
int destElement = 9;
int index = binarySearch(a,destElement);
//传入数组,和查询的值,该方法如果找到指定元素就返回下标,否则返回-1
//输出
System.out.println((destElement==-1?)destElement+"元素不存在!":destElement+"在数组的下标是"+index);
}
//定义二分法
public static int binarySearch(int[] a,destElement){
int begin = 0;
int end = a.length-1;
while(begin<=end){//当begin不等于end,会一直执行
int mid = (begin+end)/2;
if (a[mid]==destElement){
return mid;
}else if(a[mid]>destElement){
end= mid - 1;
}else if(a[mid]<destElement){
begin =mid +1;
}
}
return -1; //没有找到
}
}
//二分查找优化,当数组中有多个要查找的值时,如何将所有的元素都查找到(有序序列)
//思路当找到返回值后,向左边和右边寻找相同的值
import java.util.ArrayList;
import java.util.List;
public class suibian {
public static void main(String[] args) {
int[] a = {1,2,3,4,7,9,9,9,75,75,75,100};
List<Integer> list= binarySearch(a,0,a.length,75);
System.out.println(list);
}
public static ArrayList<Integer> binarySearch(int[] arr, int left, int right, int findVal){
if(left>right||findVal<arr[0]||findVal>arr[arr.length-1]){
return null;
}
int mid = (left+right)/2;
int midVal = arr[mid];
if(findVal>midVal){
return binarySearch(arr,mid+1,right,findVal);
}else if(findVal<midVal){
return binarySearch(arr,left,mid-1,findVal);
}else{
ArrayList<Integer> resIndexList = new ArrayList<>();
int temp = mid -1;
while(true){
if(temp<0||arr[temp]!=findVal){
break;
}
resIndexList.add(temp);
temp-=1;
}
resIndexList.add(mid);//把二分法找到的索引也加入list
//向右边扫描
temp = mid +1;
while(true){
if(temp>arr.length-1||arr[temp]!=findVal){
break;
}
resIndexList.add(temp);
temp++;
}
return resIndexList;
}
}
}
//是二分法的改进版,数组同样需要有序
//代码类似,但是改进的mid的取值算法
public static ArrayList<Integer> binarySearch(int[] arr, int left, int right, int findVal){
//判断findVal的值,防止mid越界
if(left>right||findVal<arr[0]||findVal>arr[arr.length-1]){
return null;
}
int mid = left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]);
int midVal = arr[mid];
if(findVal>midVal){
return binarySearch(arr,mid+1,right,findVal);
}else if(findVal<midVal){
return binarySearch(arr,left,mid-1,findVal);
}else{
ArrayList<Integer> resIndexList = new ArrayList<>();
int temp = mid -1;
while(true){
if(temp<0||arr[temp]!=findVal){
break;
}
resIndexList.add(temp);
temp-=1;
}
resIndexList.add(mid);//把二分法找到的索引也加入list
//向右边扫描
temp = mid +1;
while(true){
if(temp>arr.length-1||arr[temp]!=findVal){
break;
}
resIndexList.add(temp);
temp++;
}
return resIndexList;
}
}
}
斐波那契数列递归方法
输入一个n ,输出斐波那契数列第n项的数字
public class Solution {
public int Fibonacci(int n) {
if(n==0){
return 0;}
if(n==1){
return 1;}
return Fibonacci(n-1) + Fibonacci(n-2);}
}
返回数组中出现次数超过数组长度一半的数字
package 算法.返回数组中出现次数超过数组长度一半的数字;
public class Solution {
public static void main(String[] args) {
int [] arr = {1,2,2,2,3,4,5,2,2,};
int res=function(arr);
System.out.println(res);
}
public static int function(int[] arr){
int size = arr.length;
for (int i = 0; i <arr.length-1 ; i++) {
for (int j = 0; j < arr.length-i-1; j++) {
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j]= arr[j+1];
arr[j+1]=temp;
}
}
}
int mid = arr.length/2;
return arr[mid];
}
}
进阶
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
Map<Integer,Integer> map =new HashMap<>();
int len = array.length;
for (int i = 0; i <len ; i++) {
Integer count=map.get(array[i]);
if(count==null){
map.put(array[i],1);
}else {
map.put(array[i],count+1);
}
}
int res = 0;
for (int b:map.keySet()) {
if (map.get(b)>=(len/2)+1){
res=b;
}
}
return res;
}
}
//冒泡算法(从小到大)(可以有相等的数值)
package 算法;
public class MaoPaoSort {
public static void main(String[] args) {
int []array = {4,5,2,11,6,3};
for (int i = 0; i <array.length-1 ; i++) {
for (int j = 0; j < array.length-i-1; j++) {
int temp;
if(array[j]>array[j+1]){
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
for (int i = 0; i <array.length ; i++) {
System.out.println(array[i]);
}
}
}
选择排序(从小到大)(可以有相等的数值)
package 算法;
public class SelectSort {
public static void main(String[] args) {
int []array ={7,5,9,2,3,4,4};
for (int i = 0; i < array.length-1; i++) {
int min = i;
for (int j = i; j <array.length-1 ; j++) {
if(array[min]>array[j+1]){
min=j+1;
}
}
if(i!=min){
int temp;
temp=array[i];
array[i]=array[min];
array[min]=temp;
}
}
for (int i = 0; i <array.length ; i++) {
System.out.println(array[i]);
}
}
}
//二分法
//二分法查找 :查找指定值的元素
// 该方法建立在数组已经排序的基础上
// 下面程序基于从小到大排序分析
//且数组中没有重复的元素
//二分法查找 :查找指定值的元素
// 该方法建立在数组已经排序的基础上
// 下面程序基于从小到大排序分析
//且数组中没有重复的元素
public class Test{
public static void main(String[] args){
int[] a = {1,2,3,4,7,9,45,75,100};
int destElement = 9;
int index = binarySearch(a,destElement);
//传入数组,和查询的值,该方法如果找到指定元素就返回下标,否则返回-1
//输出
System.out.println((destElement==-1?)destElement+"元素不存在!":destElement+"在数组的下标是"+index);
}
//定义二分法
public static int binarySearch(int[] a,destElement){
int begin = 0;
int end = a.length-1;
while(begin<=end){//当begin不等于end,会一直执行
int mid = (begin+end)/2;
if (a[mid]==destElement){
return mid;
}else if(a[mid]>destElement){
end= mid - 1;
}else if(a[mid]<destElement){
begin =mid +1;
}
}
return -1; //没有找到
}
}
//题目描述
//在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
public class Solution {
public boolean Find(int target, int [][] array) {
/*
思路:
根据数组的特点可以发现,如果从左下角开始查找更为方便
左下角的数比同一列的数大,同时比同一行的数小
也就是从左下角开始比,如果target传入的数大于它则列数加一继续比较
如果小于它则行数减一继续比较
*/
//定义行数
int rows=array.length;
//定义列数
int lies=array[0].length;
//定义i用来技计数
int i=0;
//while里的条件是极限情况,不满足条件的时候则停止
while((rows>0)&&(i<lies))
{
//目标大于左下角,让列数自增
if(target>array[rows-1][i])
{
i++;
//目标小于左下角,让行数自减
}else if(target<array[rows-1][i])
{
rows--;
}else
{
//除了上述两种情况就是相等了,说明已经找到了
return true;
}
}
//遍历完还没有找到,说明不存在
return false;
}
}
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
//replace(int start, int end, String str):将起始位置为start,结束位置为end-1的子串替换为str。不生成新的StringBuilder对象,在原来的StringBuilder对象上修改。
public class Solution {
public String replaceSpace(StringBuffer str) {
int index = str.indexOf(" ");
while(index != -1){
str.replace(index,index+1,"%20");
index = str.indexOf(" ",index);//查找指定字符或字符串在字符串中第一次出现地方的索引,未找到的情况返回 -1.
// 从index的地方开始找,返回第一次出现的索引
}
String result = str.toString();
return result;
}
}
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
这里要使用栈,利用栈先进后出的方法
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.Stack;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack = new Stack<>();
while (listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}
ArrayList<Integer> list = new ArrayList<>();
while (!stack.isEmpty()) {
list.add(stack.pop());
}
return list;
}
}
用两个栈实现队列
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
while(!stack1.empty()){
stack2.push(stack1.peek());
stack1.pop();
}
int value = stack2.peek();
stack2.pop();
while(!stack2.empty()){
stack1.push(stack2.peek());
stack2.pop();
}
return value;
}
}
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
//先根据题目要求判断数组的大小为0则返回0
if(array.length==0){
return 0;
}
int left=0;
int right=array.length-1;
while(left<right){
int mid=(left+right)/2;
if(array[mid]>array[right]){
left=mid+1;
//在这一步不能直接使用经典的二分法思想
}else if(array[mid]<array[right]){
right--;;
}else{
right=mid;
}
}
return array[left];
}
}
题目描述
给出一个正整数N和长度L,找出一段长度大于等于L的连续非负整数,他们的和恰好为N。答案可能有多个,我我们需要找出长度最小的那个。
例如 N = 18 L = 2:
5 + 6 + 7 = 18
3 + 4 + 5 + 6 = 18
都是满足要求的,但是我们输出更短的 5 6 7
输入描述:
输入数据包括一行: 两个正整数N(1 ≤ N ≤ 1000000000),L(2 ≤ L ≤ 100)
输出描述:
从小到大输出这段连续非负整数,以空格分隔,行末无空格。如果没有这样的序列或者找出的序列长度大于100,则输出No
思路 对于连续序列求和 有高斯公式 sum = (start +end)*(end-start+1)/2
所以sum=(start+start+L-1)*L/2
package 算法.求序列和;
public class Function {
public static void main(String[] args) {
String s = getLine(18,2);
System.out.println(s);
}
public static String getLine(long n,int L){
if(n<1||n>1000000000||L<2||L>=100){
return null;
}
long start =0;
String line="";
for (int i =L ; i <=100 ; i++) {
// n=(2*start+L-1)*L/2
long doubleStart = n*2/i-i+1;
//如果start为整数,说明找到了最短序列
if(doubleStart%2==0){
start=doubleStart/2;
for (int j = 0; j <i ; j++) {
if (j<i-1){
line+=start+j+" ";
}else {
//如果是最后一个不输入空格
line+=start+j;
}
}
return line;
}
}
return "NO";
}
}
斐波那契数列递归方法
输入一个n ,输
出斐波那契数列第n项的数字
public class Solution {
public int Fibonacci(int n) {
if(n==0){
return 0;}
if(n==1){
return 1;}
return Fibonacci(n-1) + Fibonacci(n-2);}
}
package 算法.剑指offer.根据前序遍历和中序遍历创建二叉树.hhh;
/*因为是树的结构,一般都是用递归来实现。
用数学归纳法的思想就是,假设最后一步,就是root的左右子树都已经重建好了,那么我只要考虑将root的左右子树安上去即可。
根据前序遍历的性质,第一个元素必然就是root,那么下面的工作就是如何确定root的左右子树的范围。
根据中序遍历的性质,root元素前面都是root的左子树,后面都是root的右子树。那么我们只要找到中序遍历中root的位置,就可以确定好左右子树的范围。
正如上面所说,只需要将确定的左右子树安到root上即可。递归要注意出口,假设最后只有一个元素了,那么就要返回。000000
* */
import java.util.Arrays;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
//数组长度为0的时候要处理
if(pre.length == 0){
return null;
}
int rootVal = pre[0];
//数组长度仅为1的时候就要处理
if(pre.length == 1){
return new TreeNode(rootVal);
}
//我们先找到root所在的位置,确定好前序和中序中左子树和右子树序列的范围
TreeNode root = new TreeNode(rootVal);
int rootIndex = 0;
for(int i=0;i<in.length;i++){
if(rootVal == in[i]){
rootIndex = i;
break;
}
}
//递归,假设root的左右子树都已经构建完毕,那么只要将左右子树安到root左右即可
//这里注意Arrays.copyOfRange(int[],start,end)是[)的区间
root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,rootIndex+1),Arrays.copyOfRange(in,0,rootIndex));
root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,rootIndex+1,pre.length),Arrays.copyOfRange(in,rootIndex+1,in.length));
return root;
}
public static void main(String[] args) {
int [] pre = {1,2,4,7,3,5,6,8};
int [] mid= {4,7,2,1,5,3,8,6};
TreeNode root =new Solution().reConstructBinaryTree(pre,mid);
root.preOrder();
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
//编写一个前序遍历的方法用于遍历
public void preOrder(){
System.out.println(this);
if(this.left!=null){
this.left.preOrder();
}
if (this.right!=null){
this.right.preOrder();
}
}
@Override
public String toString() {
return "TreeNode{" +
"val=" + val +
'}';
}
}
跳台阶问题
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
比较倾向于找规律的解法,f(1) = 1, f(2) = 2, f(3) = 3, f(4) = 5, 可以总结出f(n) = f(n-1) + f(n-2)的规律,但是为什么会出现这样的规律呢?假设现在6个台阶,我们可以从第5跳一步到6,这样的话有多少种方案跳到5就有多少种方案跳到6,另外我们也可以从4跳两步跳到6,跳到4有多少种方案的话,就有多少种方案跳到6,其他的不能从3跳到6什么的啦,所以最后就是f(6) = f(5) + f(4);这样子也很好理解变态跳台阶的问题了
public class Solution {
public int JumpFloor(int target) {
int n = target;
if (n==1){
return 1;
}
if (n==2){
return 2;
}
return JumpFloor(n-1)+JumpFloor(n-2);
}
}
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
每个台阶可以看作一块木板,让青蛙跳上去,n个台阶就有n块木板,最后一块木板是青蛙到达的位子, 必须存在,其他 (n-1) 块木板可以任意选择是否存在,则每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法,可以直接得到结果。
public class Solution {
public int JumpFloorII(int target) {
if(target <= 0)
return 0;
int result = 1;
while(target > 1){
result = 2 * result;
target--;
}
return result;
}
}
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
public class Solution {
public static int NumberOf1(int n) {
// 返回指定值的二进制,用字符串保存
String str = Integer.toBinaryString(n);
int count=0;
//String.toCharArray()把字符串变成char[]
for (char b:str.toCharArray()) {
if(b=='1'){
count++;
}
}
return count;
}
}
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
public class Solution {
public double Power(double base, int exponent) {
double result = base;
int n = exponent;
if (exponent < 0) {
exponent = - exponent;
}
else if(exponent == 0) {
return 1;
}
for (int i = 1; i < exponent; i++) {
result *= base;
}
return n < 0 ? 1 / result : result;
}
}
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
package 算法.剑指offer;
import java.util.ArrayList;
import java.util.List;
public class Solution {
public void reOrderArray(int [] array) {
List<Integer> list1 =new ArrayList<>();
List<Integer> list2 =new ArrayList<>();
int temp=0;
for (int i = 0; i <array.length ; i++) {
if(array[i]%2==1){
list1.add(array[i]);
}else {
list2.add(array[i]);
}
}
for (int a:list1) {
array[temp]=a;
temp++;
}
temp=list1.size();
for (int b:list2) {
array[temp]=b;
temp++;
}
}
}
//方法二 插入排序思想
package 算法.剑指offer;
import java.util.Arrays;
public class Solution {
public void reOrderArray(int [] array) {
//相对位置不变,稳定性
//插入排序的思想
int m = array.length;
int k = 0;//记录已经摆好位置的奇数的个数
for (int i = 0; i < m; i++) {
if (array[i] % 2 == 1) {
int j = i;
while (j > k) {//j >= k+1
int tmp = array[j];
array[j] = array[j-1];
array[j-1] = tmp;
j--;
}
k++;
}
}
}
public static void main(String[] args) {
int [] arr = {1,7,2,9,5,4,6,11};
Solution solution = new Solution();
solution.reOrderArray(arr);
System.out.println(Arrays.toString(arr));
}
}
输入一个链表,反转链表后,输出新链表的表头。
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null){
return null;
}
ListNode pre = null;
ListNode temp = null;
while (head!=null){
//用temp保存head的下一个结点
temp=head.next;
//让当前结点指向pre,反转链表
head.next=pre;
//pre变成当前结点
pre=head;
head=temp;
}
return pre;
}
}
import java.util.Stack;
class Solution {
public ListNode ReverseList(ListNode head) {
if (head==null){
return null;
}
Stack<ListNode> stack = new Stack<>();
while (head!=null){
stack.push(head);
head =head.next;
}
head = stack.pop();
ListNode temp = head ;
while (!stack.empty()){
temp.next=stack.pop();
temp=temp.next;
}
//防止链表成环
temp.next=null;
return head;
}
}
贪心算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4YQGA5c9-1584535809922)(E:\笔记\图片\1584282191146.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGxGaybX-1584535809922)(E:\笔记\图片\1584282227811.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDIEC1KK-1584535809924)(E:\笔记\图片\1584282402554.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7rGT2MIW-1584535809925)(E:\笔记\图片\1584282526487.png)]
//贪心算法不一定是最优解,注意事项
//代码
package 算法.贪心算法;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class GreedyAlgorighm {
public static void main(String[] args) {
//创建广播电台,用map存放 key是广播台编号,value是一个set(无序唯一) 存放着覆盖的地区信息
HashMap<String, HashSet<String>> broadCasts= new HashMap<String,HashSet<String>>();
HashSet<String> hashSet1 = new HashSet<String>();
hashSet1.add("北京");
hashSet1.add("上海");
hashSet1.add("天津");
HashSet<String> hashSet2 = new HashSet<String>();
hashSet2.add("广州");
hashSet2.add("北京");
hashSet2.add("深圳");
HashSet<String> hashSet3 = new HashSet<String>();
hashSet3.add("上海");
hashSet3.add("成都");
hashSet3.add("杭州");
HashSet<String> hashSet4 = new HashSet<String>();
hashSet4.add("上海");
hashSet4.add("天津");
HashSet<String> hashSet5 = new HashSet<String>();
hashSet5.add("杭州");
hashSet5.add("大连");
//加入map
broadCasts.put("K1",hashSet1);
broadCasts.put("K2",hashSet2);
broadCasts.put("K3",hashSet3);
broadCasts.put("K4",hashSet4);
broadCasts.put("K5",hashSet5);
//allAress 存放当前还没覆盖的地区
HashSet<String> allAress =new HashSet<String>();
allAress.add("北京");
allAress.add("上海");
allAress.add("天津");
allAress.add("广州");
allAress.add("深圳");
allAress.add("成都");
allAress.add("杭州");
allAress.add("大连");
//创建ArrayList 存放被选中的天台的集合
ArrayList<String> select = new ArrayList<String>();
//定义一个临时的集合,在遍历的过程中,存放遍历过程中的电台覆盖区域和还未覆盖地区的交集
HashSet<String> tempSet = new HashSet<String>();
//定义maxKey,保存在一次遍历过程中,能够覆盖最大未覆盖的地区对应的电台的key
//如果maxKey不为null,则会加入到select中
String maxKey = null;
while (allAress.size()!=0){
//如果allAress不为0,表示还没有覆盖所有的地区
//遍历broadCast,取出对应key
//每次循环需要把maxKey清空,防止temp.size还大于0时,因为temp.size 小于broadCasts.get(maxKey).size()而不进行maxKey的重置
maxKey=null;
for (String key:broadCasts.keySet()) {
//每一次进行for 必须将tempSet清空
tempSet.clear();
//当前这个key能覆盖到的地区
HashSet<String> areas = broadCasts.get(key);
tempSet.addAll(areas);
//求出tempSet和allAreas集合的交集,会把交集赋值给tempSet
tempSet.retainAll(allAress);
//如果当前这个集合包含的未覆盖地区数量,比maxkey指向的集合的地区还多,就把maxKey指向这个集合
if(tempSet.size()>0&&(maxKey==null||tempSet.size()>broadCasts.get(maxKey).size())){
maxKey=key;
}
}
if(maxKey!=null) {
select.add(maxKey);
//将maxKey指向的地区从allAreas去掉
allAress.removeAll(broadCasts.get(maxKey));
}
}
System.out.println("得到的结果是"+select);
}
}
KMP算法 字符串匹配问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3TWYWdTH-1584535809925)(E:\笔记\图片\1584324756964.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3om4AEfX-1584535809926)(E:\笔记\图片\1584324909511.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bw4LykH2-1584535809927)(E:\笔记\图片\1584326157747.png)]
部分匹配值的表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNY3oMGX-1584535809927)(E:\笔记\图片\1584325992548.png)]
next记录的是当前字符串前缀和后缀匹配的个数
package 算法.KMPAlgorithm;
import java.util.Arrays;
public class Kmp {
public static void main(String[] args) {
String str1 = "BBC ABCDAB ABCDABCDABDE";
String str2 ="ABCDABD";//结果15
int[] next=kmpNext(str2);
//输出部分匹配值表
System.out.println("next:"+ Arrays.toString(next));
//匹配字符串,返回索引值
System.out.println(kmpSearch(str1,str2,next));
}
//获取字串的字符匹配值表
public static int[] kmpNext(String str){
//创建一个next表,每个下标存储的值是部分匹配值,也可以理解为前缀与后缀最大相同的·1个数
int [] next = new int[str.length()];
//第一个字符的匹配值一定为0
next[0]=0;
for (int i = 1,j=0; i <str.length() ; i++) {
//如果str.charAt(i)!=str.charAt(j)
while(j>0&&str.charAt(i)!=str.charAt(j)){
j=next[j-1];
}
//当满足str.charAt(i)==str.charAt(j),部分匹配值加一
if(str.charAt(i)==str.charAt(j)){
j++;
}
next[i]=j;
}
return next;
}
//得到字串的字符串匹配表后,我们可以开始写kmp算法了
//参数 第一个是源字符串,第二个是要匹配的字符串 数组是字串2的部分匹配值表
//如果匹配返回第一个匹配值的索引,如果不匹配,返回-1
public static int kmpSearch(String str1,String str2,int[] next){
for (int i = 0,j=0; i <str1.length() ; i++) {
while (j>0&&str1.charAt(i)!=str2.charAt(j)){
j=next[j-1];
}
if(str1.charAt(i)==str2.charAt(j)){
j++;
}
if (j==str2.length()){
return i-j+1;
}
}
return -1;
}
}
分治算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0KoRAVmE-1584535809928)(E:\笔记\图片\1584340379464.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zjnjkanl-1584535809928)(E:\笔记\图片\1584340439795.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3R2YZZg-1584535809929)(E:\笔记\图片\1584340511467.png)]
分治算法之汉诺塔
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qt9PWbuC-1584535809930)(E:\笔记\图片\1584341034562.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BoX6IC73-1584535809930)(E:\笔记\图片\1584341276408.png)]
package 算法.分治算法之汉诺塔;
public class HanNuoTower {
public static void main(String[] args) {
HanNuo(3,'a','b','c');
}
public static void HanNuo(int num,char a,char b,char c){
if(num==1){
System.out.println("第1个盘从"+a+"->"+c);
}else {
//把上面的所有盘移动到b,移动中利用c塔
HanNuo(num-1,a,c,b);
//把最下面的盘从a移动到c
System.out.println("第"+num+"个盘从"+a+"->"+c);
//把b塔所有盘从b移动到c,移动中利用a塔
HanNuo(num-1,b,a,c);
}
}
}
动态规划算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FUZaycHB-1584535809931)(E:\笔记\图片\1584339893690.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1RvwYJlS-1584535809931)(E:\笔记\图片\1584344535186.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMtjx35Y-1584535809932)(E:\笔记\图片\1584344622777.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcCpehE8-1584535809932)(E:\笔记\图片\1584347355265.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m3yxA97J-1584535809933)(E:\笔记\图片\1584347076058.png)]
package 算法.动态规划之背包问题;
public class bag {
public static void main(String[] args) {
//物品的重量
int[] w = {1,4,3}; //0是对应表格的物品为0磅的情况
int[] val={1500,3000,2000};//物品价格
int bagWeight = 4 ;//背包重量
int n = val.length ; //物品个数
//创建二维数组
//v[i][j]表示 前i个物品中能够装入容量为j的背包的最大价值
//n+1,m+1是因为表格第0行和第0列都为0
int[][] v= new int[n+1][bagWeight+1];
//定义一个二维数组记录商品的存放情况
int[][] path = new int[n+1][bagWeight+1];
//第0列初始化
for (int i = 0; i <v.length ; i++) {
v[i][0] =0;
}
//第0行初始化
for (int i = 0; i <v[0].length ; i++) {
v[0][i]=0;
}
//根据前面的公式进行动态规划处理
// i和j都是1 因为0行0列已经初始化
for (int i = 1; i <v.length ; i++) {
for (int j = 1; j <v[0].length ; j++) {
//如果当前物品大于背包当前容量j,那么当前最大价值为
if(w[i-1]>j){
v[i][j]=v[i-1][j];
}else {
//否则 v[i][j] =max{val[i-1]+v[i-1][j-w[i-1]],v[i-1][j]]}
if(v[i-1][j]<val[i-1]+v[i-1][j-w[i-1]]){
//满足上面条件,表明增加当前商品选择后,有更好的方案
v[i][j]=val[i-1]+v[i-1][j-w[i-1]];
//把当前的情况记录到patj
path[i][j]=1;
}else {
v[i][j]=v[i-1][j];
}
}
}
}
//测试一下输出
for (int i = 0; i <v.length ; i++) {
System.out.println("\n");
for (int j = 0; j <v[0].length ; j++) {
System.out.print(v[i][j]);
System.out.print(" ");
}
}
//输出最高价值,并显示最佳的搭配方式
int i =path.length-1;
int j = path[0].length-1;
//从path的最后开始找
System.out.println("最大价值为:"+v[i][j]);
while (i>0&&j>0){
//path[][]=1 的值都是增加当前商品后的最好方案
if(path[i][j]==1){
System.out.println("第"+i+"个商品放入背包");
//一个商品只能放一次,放入后,j减去商品重量
j=j-w[i-1];
}
i--;
}
}
}
稀疏数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YaCxrp29-1584535809933)(E:\笔记\图片\1584510271658.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTtaHH2c-1584535809934)(E:\笔记\图片\1584510483882.png)]
稀疏数组第一行记录原表的行数 列数 特殊值个数
下面分别记录每个特殊值的所在位置和值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m2fjEfHi-1584535809936)(E:\笔记\图片\1584510771963.png)]
package 算法.稀疏数组;
public class SparseArray {
public static void main(String[] args) {
int [][] testArr = new int[11][11];
testArr[1][1]=1;
testArr[2][3]=3;
testArr[3][4]=6;
int[][] SparseArray = getSparseArray(testArr,0);
for (int i = 0; i <SparseArray.length ; i++) {
for (int j = 0; j <SparseArray[0].length ; j++) {
System.out.print(SparseArray[i][j]+"\t");
}
System.out.println(" ");
}
System.out.println("");
int[][] resArr = getArray(SparseArray,0);
for (int i = 0; i <resArr.length ; i++) {
for (int j = 0; j <resArr[0].length ; j++) {
System.out.print(resArr[i][j]+"\t");
}
System.out.println(" ");
}
}
//接收普通数组,和数组的大多数默认值,返回稀疏数组
public static int[][] getSparseArray(int arr[][],int defautNum){
//获取普通数组的特殊值个数
int count =0;
int temp=1;
for (int i = 0; i <arr.length ; i++) {
for (int j = 0; j <arr[0].length ; j++) {
if(arr[i][j]!=defautNum){
count++;
}
}
}
int[][] SparseArray=new int[count+1][3];
SparseArray[0][0]=arr.length;
SparseArray[0][1]=arr[0].length;
SparseArray[0][2]=count;
for (int i = 0; i <arr.length ; i++) {
for (int j = 0; j <arr[0].length ; j++) {
if(arr[i][j]!=defautNum){
SparseArray[temp][0]=i;
SparseArray[temp][1]=j;
SparseArray[temp][2]=arr[i][j];
temp++;
}
}
}
return SparseArray;
}
//接收稀疏数组,返回普通数组
public static int[][] getArray(int[][] sparseArr,int defalutNum){
int arr[][]= new int[sparseArr[0][0]][sparseArr[0][1]];
for (int i = 0; i <arr.length ; i++) {
for (int j = 0; j <arr[0].length ; j++) {
arr[i][j]=defalutNum;
}
}
for (int i = 1; i <sparseArr.length ; i++) {
arr[sparseArr[i][0]][sparseArr[i][1]]=sparseArr[i][2];
}
return arr;
}
}
数组模拟队列**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHk1DDfb-1584535809936)(E:\笔记\图片\1584379322297.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-btAEKIGC-1584535809936)(E:\笔记\图片\1584379351004.png)]
package 算法.用数组模拟队列;
import java.util.Scanner;
public class ArrayQueueDemo {
public static void main(String[] args) {
//创建一个队列
ArrayQueue queue= new ArrayQueue(3);
char key = ' ';//接收用户输入
Scanner scanner= new Scanner(System.in);
boolean loop = true;
//输出菜单
while (loop){
System.out.println("输入s (show):显示队列");
System.out.println("输入e (exit):退出程序");
System.out.println("输入a (add):添加数据");
System.out.println("输入g (get):取出数据");
System.out.println("输入h (head):查看头数据");
key = scanner.next().charAt(0);
switch (key){
case 's':
queue.show();;
break;
case 'a':
System.out.println("输入一个数");
int value =scanner.nextInt();
queue.add(value);
break;
case 'e':
System.out.println("程序退出");
return;
case 'g':
try {
int res = queue.getQueue();
System.out.println("取出的数据是"+res);
}catch (Exception e){
e.printStackTrace();
}
break;
case 'h':
try {
int res = queue.headQueue();
System.out.println("队列头数据是"+res);
}catch (Exception e){
e.printStackTrace();
}
break;
default:
break;
}
}
}
}
class ArrayQueue{
private int maxSize;//定义数组最大容量
private int front;//队列头
private int rear;//队列尾
private int[] arr; //存放数据的数组,模拟队列
//创建队列的构造器
public ArrayQueue(int maxSize){
this.maxSize=maxSize;
arr=new int[maxSize];
front=-1;//指向队列头的前一个位置
rear=-1;
}
public boolean isFull(){
return rear==maxSize-1;
}
public boolean isEmpty(){
return rear==front;
}
//添加数据
public void add(int n){
if(isFull()){
System.out.println("队列满无法加入");
}else {
rear++;
arr[rear]=n;
}
}
//获取队列头数据并且出队
public int getQueue(){
if(isEmpty()){
throw new RuntimeException("队列已经空");
}else {
front++;
return arr[front];
}
}
public void show(){
//显示队列数据
if(isEmpty()){
System.out.println("队列空");
return;
}
for (int i = 0; i <arr.length ; i++) {
System.out.printf("arr[%d]=%d",i,arr[i]);
System.out.println("");
}
}
//显示队列头数据
public int headQueue(){
if(isEmpty()){
throw new RuntimeException("当前数据为空,无法获取头部数据");
}else {
return arr[front+1];
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktYqMb3d-1584535809937)(E:\笔记\图片\1584383034054.png)]
环形队列
//有个位置是空的,而且会动态变化,尾指针一直指向该位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qcn6OcbO-1584535809938)(E:\笔记\图片\1584415996754.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0rLdTqLb-1584535809938)(E:\笔记\图片\1584416145791.png)]
package 算法.用数组模拟队列;
import java.util.Scanner;
public class CircleArrayQueuedemo {
public static void main(String[] args) {
//创建一个队列
CircleArrayQueue queue= new CircleArrayQueue(4);
char key = ' ';//接收用户输入
Scanner scanner= new Scanner(System.in);
boolean loop = true;
//输出菜单
while (loop){
System.out.println("输入s (show):显示队列");
System.out.println("输入e (exit):退出程序");
System.out.println("输入a (add):添加数据");
System.out.println("输入g (get):取出数据");
System.out.println("输入h (head):查看头数据");
key = scanner.next().charAt(0);
switch (key){
case 's':
queue.show();;
break;
case 'a':
System.out.println("输入一个数");
int value =scanner.nextInt();
queue.add(value);
break;
case 'e':
System.out.println("程序退出");
return;
case 'g':
try {
int res = queue.getQueue();
System.out.println("取出的数据是"+res);
}catch (Exception e){
e.printStackTrace();
}
break;
case 'h':
try {
int res = queue.headQueue();
System.out.println("队列头数据是"+res);
}catch (Exception e){
e.printStackTrace();
}
break;
default:
break;
}
}
}
}
class CircleArrayQueue{
private int maxSize;//定义数组最大容量
private int front;//队列头
private int rear;//队列尾
private int[] arr; //存放数据的数组,模拟队列
//创建队列的构造器
public CircleArrayQueue(int maxSize){
this.maxSize=maxSize;
arr=new int[maxSize];
front=0;//头指针指向队列第一个元素
rear=0;//尾指针指向队列最后一个元素的后一位,留一个空间
}
public boolean isFull(){
return (rear+1)%maxSize==front;
}
public boolean isEmpty(){
return rear==front;
}
//添加数据
public void add(int n){
if(isFull()){
System.out.println("队列满无法加入");
}else {
arr[rear]=n;
rear=(rear+1)%maxSize;
}
}
//取队列头数据并且出队
public int getQueue(){
if(isEmpty()){
throw new RuntimeException("队列已经空");
}else {
int value = arr[front];
front=(front+1)%maxSize;
return value;
}
}
public void show(){
//显示队列数据
//从front开始遍历
//个数为 (rear+maxSeze-front)%maxSize
if(isEmpty()){
System.out.println("队列空");
return;
}
for (int i = front; i <front+(rear+maxSize-front)%maxSize; i++) {
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
//显示队列头数据
public int headQueue(){
if(isEmpty()){
throw new RuntimeException("当前数据为空,无法获取头部数据");
}else {
return arr[front+1];
}
}
}
单链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7k6VdYDN-1584535809939)(E:\笔记\图片\1584421756115.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JecYHnZv-1584535809939)(E:\笔记\图片\1584423836606.png)]
package 算法.单向链表的创建和遍历;
public class LinkedListDemo {
public static void main(String[] args) {
HeroNode node1 = new HeroNode(1,"宋江");
HeroNode node2 = new HeroNode(2,"卢俊义");
HeroNode node3 = new HeroNode(3,"吴用");
HeroNode node4 = new HeroNode(4,"林冲");
LinkList list = new LinkList();
list.add(node1);
list.add(node2);
list.add(node3);
list.add(node4);
list.show();
}
}
//创建一个链表类
class LinkList{
//先初始化一个头节点
private HeroNode head= new HeroNode(0,"");
//添加结点
//如果不考虑编号
//找到链表的尾指针,指向添加的结点
public void add(HeroNode node){
HeroNode temp =head;
while (true){
if(temp.next==null){
break;
}
temp=temp.next;
}
temp.next=node;
}
//遍历链表
public void show(){
HeroNode temp = head.next;//本例中头节点不存放数据
if (head.next==null){
System.out.println("链表为空");
return;
}
while (true){
if(temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
}
//结点类
class HeroNode{
public int no;
public String name;
public HeroNode next;
public HeroNode(int no,String name){
this.no=no;
this.name=name;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
根据排名来添加结点,修改结点,删除结点
package 算法.单向链表的创建和遍历;
public class LinkedListDemo {
public static void main(String[] args) {
HeroNode node1 = new HeroNode(1,"宋江");
HeroNode node2 = new HeroNode(2,"卢俊义");
HeroNode node3 = new HeroNode(3,"吴用");
HeroNode node4 = new HeroNode(4,"林冲");
HeroNode node5 = new HeroNode(4,"林");
HeroNode node6 = new HeroNode(6,"456");
LinkList list = new LinkList();
list.add(node1);
list.add(node2);
list.add(node4);
list.addByNo(node3);
list.show();
System.out.println("");
list.del(4);
list.show();
}
}
//创建一个链表类
class LinkList{
//先初始化一个头节点
private HeroNode head= new HeroNode(0,"");
//添加结点
//如果不考虑编号
//找到链表的尾指针,指向添加的结点
public void add(HeroNode node){
HeroNode temp =head;
while (true){
if(temp.next==null){
break;
}
temp=temp.next;
}
temp.next=node;
}
//第二种添加英雄的方式
//根据排名将英雄加入指定的位置,如果有这个排名。则添加失败
public void addByNo(HeroNode node){
//辅助指针指向要添加的位置的上一个结点
HeroNode temp =head;
//标志当前的添加的编号是否存在
boolean flag= false;
while (true){
if (temp.next==null){
//temp已经在链表的最后
//当前结点指向要添加的结点
break;
}
if(temp.next.no>node.no){
//如果下一个结点的编号大于要添加的结点
//那就添加到当前位置
break;
}else if(temp.next.no==node.no){
flag=true;
break;
}
temp=temp.next;
}
//判断是否要添加的编号已经存在
if(flag){
System.out.println("编号已经存在");
}else {
node.next=temp.next;
temp.next=node;
}
}
//根据no修改结点的信息,no不能修改,否则会打乱编号排序,修改no应该删除结点,再重新添加。
//根据newHeronode的no值来添加修改即可
public void update(HeroNode newHeroNode){
if(this.head.next==null){
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
boolean flag = false;
while(true){
if(temp==null){
break;
}
if(temp.no==newHeroNode.no){
flag=true;
break;
}
temp=temp.next;
}
if(flag==true){
temp.name=newHeroNode.name;
}else {
System.out.println("没有找到要修改的结点");
}
}
//根据no删除结点
public void del(int no){
HeroNode temp = head;
while (head==null){
System.out.println("链表为空,无法删除");
return;
}
while(true){
if(temp.next==null){//要写在temp.next.no==nod判断语句之前,不然当temp是表尾会发生空指针异常
System.out.println("不存在指定删除的结点");
break;
}
if(temp.next.no==no){
temp.next=temp.next.next;
break;
}
temp=temp.next;
}
}
//遍历链表
public void show(){
HeroNode temp = head.next;//本例中头节点不存放数据
boolean flag=false;//flag标志添加的编号是否存在
if (head.next==null){
System.out.println("链表为空");
return;
}
while (true){
if(temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
}
//结点类
class HeroNode{
public int no;
public String name;
public HeroNode next;
public HeroNode(int no,String name){
this.no=no;
this.name=name;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
栈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hDYdqNzb-1584535809940)(E:\笔记\图片\1584438505756.png)]
package 栈;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(10);
stack.push(1);
stack.push(3);
stack.push(5);
stack.pop();
stack.show();
}
}
class ArrayStack{
private int maxSize;
private int[] stack;
private int top= -1;
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack =new int[maxSize];
}
//栈满
public boolean isFull(){
return top==maxSize-1;
}
public boolean isEmpty(){
return top==-1;
}
//入栈
public void push(int value){
if(isFull()){
System.out.println("栈已满");
return;
}
top++;
stack[top]=value;
}
public int pop(){
if(isEmpty()){
System.out.println("栈已空");
throw new RuntimeException("栈空");
}
int value= stack[top];
top--;
return value;
}
//遍历栈,从栈顶开始输出
public void show(){
if(isEmpty()){
System.out.println("栈空");
}
for (int i = top; i >=0 ; i--) {
System.out.println(stack[i]);
}
}
}
t.println(“链表为空,无法删除”);
return;
}
while(true){
if(temp.nextnull){//要写在temp.next.nonod判断语句之前,不然当temp是表尾会发生空指针异常
System.out.println(“不存在指定删除的结点”);
break;
}
if(temp.next.no==no){
temp.next=temp.next.next;
break;
}
temp=temp.next;
}
}
//遍历链表
public void show(){
HeroNode temp = head.next;//本例中头节点不存放数据
boolean flag=false;//flag标志添加的编号是否存在
if (head.next==null){
System.out.println("链表为空");
return;
}
while (true){
if(temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
}
//结点类
class HeroNode{
public int no;
public String name;
public HeroNode next;
public HeroNode(int no,String name){
this.no=no;
this.name=name;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
**栈**
[外链图片转存中...(img-hDYdqNzb-1584535809940)]
package 栈;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(10);
stack.push(1);
stack.push(3);
stack.push(5);
stack.pop();
stack.show();
}
}
class ArrayStack{
private int maxSize;
private int[] stack;
private int top= -1;
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack =new int[maxSize];
}
//栈满
public boolean isFull(){
return top==maxSize-1;
}
public boolean isEmpty(){
return top==-1;
}
//入栈
public void push(int value){
if(isFull()){
System.out.println("栈已满");
return;
}
top++;
stack[top]=value;
}
public int pop(){
if(isEmpty()){
System.out.println("栈已空");
throw new RuntimeException("栈空");
}
int value= stack[top];
top--;
return value;
}
//遍历栈,从栈顶开始输出
public void show(){
if(isEmpty()){
System.out.println("栈空");
}
for (int i = top; i >=0 ; i--) {
System.out.println(stack[i]);
}
}
}
package facetest;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
in.nextLine();
String arrayStr = in.nextLine();
String[] arr = arrayStr.split(" ");
int [] num = new int[n];
ArrayList<Integer> list =new ArrayList<>();
for (int i = 0; i < n; i++) {
num[i] = Integer.parseInt(arr[i]);
list.add(num[i]);
}
for (int i = 0; i <num.length ; i++) {
Collections.sort(list);
list.remove(i);
int result = list.get(n/2-1);
System.out.println(result);
list.add(num[i]);
}
}
}