数据结构和算法(查找算法[ 二分、插值 ]、哈希表构成、普通二叉树操作、线索化和遍历[ 前、中、后 ] 序)

常用查找算法:

  • 顺序(线性)查找
  • 二分查找/折半查找
  • 插值查找

顺序查找:

按照顺序,遍历数组,比对数字,如果找到,返回下标,由于比较简单,不再介绍

二分查找:

二分查找思路分析:
在这里插入图片描述
需要查找的数组必须是有序的,否则查找没有意义

二分查找代码实现:

public class BinarySearch {
	public static void main(String[] args) {
		int[] arr = new int[] { 1, 8, 10, 60, 89, 1000, 1234 };
		int i = binarySearch(arr, 0, arr.length - 1, 60);
		System.out.println(i);

	}
	
	//普通二分查找
	public static int binarySearch(int[] arr, int left, int right, int findValue) {
		// 递归调用出口
		if (left > right) {
			return -1;
		}
		int mid = (left + right) / 2;
		if (arr[mid] == findValue) {
			return mid;
		} else if (arr[mid] > findValue) {
			return binarySearch(arr, left, mid - 1, findValue);
		} else if (arr[mid] < findValue) {
			return binarySearch(arr, mid + 1, right, findValue);
		}

		return -1;

	}

一般二分查找只能找到一个索引,如果符合条件的数据有多个,那么就只能返回第一个符合条件数据的索引,如果要返回多个索引,需要继续查找:
二分查找实现(多个查询):

	public static int[] binarySearch2(int[] arr, int left, int right, int findValue) {

		if (left > right) {
			return result;
		}
		int mid = (left + right) / 2;
		if (arr[mid] == findValue) {
			result[index++] = mid;
			binarySearch2(arr, mid + 1, right, findValue);
			binarySearch2(arr, left, mid -1, findValue);
			return result;
			
			
		} else if (arr[mid] > findValue) {
			return binarySearch2(arr, left, mid - 1, findValue);
		} else if (arr[mid] < findValue) {
			return binarySearch2(arr, mid + 1, right, findValue);
		}
		return result;

	}

插值查找:

插值查找思路分析:
在这里插入图片描述
插值查找适用于平均分布的数据
插值查找是二分查找对平均分布数据的优化
只要将mid值得求法改成公式即可:

int mid = left + (findValue - arr[left]) / (arr[right] - arr[left]) * right;

插值查找注意事项:
在这里插入图片描述

哈希表:

哈希表介绍:
在这里插入图片描述
百度百科的图例:
在这里插入图片描述

树结构(二叉树):

在这里插入图片描述
二叉树的遍历
在这里插入图片描述

遍历思路分析:
在这里插入图片描述

二叉树遍历代码:

public class MyTree {
	public static void main(String[] args) {
		Tree tree = new Tree();
		tree.add(new TreeNode(1, "1"));
		tree.add(new TreeNode(2, "2"));
		tree.add(new TreeNode(3, "3"));
		tree.add(new TreeNode(4, "4"));
		tree.Hlist(tree.rootNode);
	}
}

//管理二叉树的类
class Tree {
	public TreeNode rootNode;

	public void add(TreeNode node) {
		if (rootNode == null) {
			rootNode = node;
			return;
		}
		TreeNode temp = rootNode;
		while (true) {
			if(temp == null) {
				break;
			}
			if (temp.id < node.id) {
				if (temp.rightChild == null) {
					temp.rightChild = node;
					break;
				} else {
					temp = temp.rightChild;
				}
			} else if (temp.id >= node.id) {
				if (temp.leftChild == null) {
					temp.leftChild = node;
					break;
				} else {
					temp = temp.leftChild;
				}
			}
		}

	}

	public void Qlist(TreeNode node) {
		if (node == null) {
			System.out.println("子树为空");
			return;
		}
		System.out.println(node);
		
		System.out.println("左子树:->");
		Qlist(node.leftChild);
		
		System.out.println("右子树:->");
		Qlist(node.rightChild);
		
		System.out.println("退回->");

	}
	
	public void Zlist(TreeNode node) {
		if (node == null) {
			System.out.println("子树为空");
			return;
		}
		
		System.out.println("左子树:->");
		Zlist(node.leftChild);
		
		System.out.println(node);
		
		System.out.println("右子树:->");
		Zlist(node.rightChild);
		
		System.out.println("退回->");

	}
	
	
	public void Hlist(TreeNode node) {
		if (node == null) {
			System.out.println("子树为空");
			return;
		}
		
		System.out.println("左子树:->");
		Hlist(node.leftChild);
		
		System.out.println("右子树:->");
		Hlist(node.rightChild);
		
		System.out.println(node);
		
		System.out.println("退回->");

	}
}

class TreeNode {
	public int id;
	public String name;
	public TreeNode rightChild;
	public TreeNode leftChild;

	public TreeNode(int id, String name) {
		this.id = id;
		this.name = name;
	}

	@Override
	public String toString() {
		return "TreeNode [id=" + id + ", name=" + name + "]";
	}

}

二叉树删除节点:

在这里插入图片描述
步骤(遍历比对):
在这里插入图片描述
遍历判断效率低,应该根据待删除的编号找到节点的父节点后将其删除:

	//调用时传入头节点,和待删除的节点编号
	public void del(TreeNode node, int id) {
		if (node.rightChild == null && node.leftChild == null) {
			System.out.println("没有找到");
			return;
		}
		if(node.id == id) {
			rootNode = null;
		}
		if (node.id < id) {
			if (node.rightChild != null) {
				if (node.rightChild.id == id) {
					node.rightChild = null;
					return;
				} else {
					del(node.rightChild, id);
				}
			}
		}
		if (node.id > id) {
			if (node.leftChild != null) {
				if (node.leftChild.id == id) {
					node.leftChild = null;
					return;
				} else {
					del(node.leftChild, id);
				}
			}
		}

	}

顺序存储二叉树:

在这里插入图片描述
在这里插入图片描述

顺序二叉树遍历:

package tree;

public class OrderTree {
	public static void main(String[] args) {
		//定义二叉树
		int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		Zlist(arr, 0, 0, arr.length - 1);

	}

	// 前序
	public static void Qlist(int[] arr, int current, int begin, int end) {

		if (arr == null || arr.length == 0) {
			return;
		}

			if (current < begin || current > end) {
				return;
			}

		System.out.print(arr[current] + " ");

		Qlist(arr, current * 2 + 1, begin, end);

		Qlist(arr, current * 2 + 2, begin, end);

	}

	public static void Zlist(int[] arr, int current, int begin, int end) {
		
		if (arr == null || arr.length == 0) {
			return;
		}

		if (current < begin || current > end) {
			return;
		}

		Zlist(arr, current * 2 + 1, begin, end);

		System.out.print(arr[current] + " ");

		Zlist(arr, current * 2 + 2, begin, end);

	}

	public static void Hlist(int[] arr, int current, int begin, int end) {
		
		if (arr == null || arr.length == 0) {
			return;
		}

		if (current < begin || current > end) {
			return;
		}

		Hlist(arr, current * 2 + 1, begin, end);

		Hlist(arr, current * 2 + 2, begin, end);

		System.out.print(arr[current] + " ");

	}
}

线索二叉树:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200401185307470.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwOTYzMDc2,size_16,color_FFFFFF,t_70)

在这里插入图片描述
如上图,我们需要右一个指针指向遍历的前一个结点才可以构建线索二叉树(在原二叉树的基础上)

实现代码:

结点结构:

class TreeNode2 {
	public int id;
	public String name;
	public TreeNode2 right;
	public TreeNode2 left;
	// leftType == 0表示指向的是左子树(默认),如果1则表示指向前驱节点
	// 如果rightType ==0表示指向是右子树(默认),如果1表示指向后继节点
	
	//用于遍历
	public int leftType;
	public int rightType;

	public TreeNode2(int id, String name) {
		this.id = id;
		this.name = name;
	}

	@Override
	public String toString() {
		return "TreeNode2 [id=" + id + ", name=" + name + "]";
	}

}

中序线索化和遍历:

	public TreeNode2 rootNode;
	public TreeNode2 preNode = null;
	/**
	 * 中序线索化,传入头节点
	 * 
	 * @param node 需要线索化的节点
	 */
	public void ZthreadNodes(TreeNode2 node) {

		if (node == null) {
			return;
		}
		// 线索化左子树
		if (node.leftType != 1)
			ZthreadNodes(node.left);
///////////////////////////////////////////////////////

		// node的值依次为[8,3,10,1,14,6]
		// preNode的值依次为[null,8,3,10,1,14]

		// 线索化当前节点
		// 处理前驱
		if (node.left == null) {
			// 当前结点的左指针指向前驱节点
			node.left = preNode;
			node.leftType = 1;
		}

		// 处理后继结点
		// 如上述分析preNode的值可能为null,需要判断
		if (preNode != null && preNode.right == null) {
			// 前驱结点的右指针指向当前结点
			preNode.right = node;
			preNode.rightType = 1;

		}

		// 每处理一个结点后,让当前结点为下一个结点的前驱结点
		preNode = node;
//////////////////////////////////////
		// 线索化右子树
		if (node.rightType != 1)
			ZthreadNodes(node.right);
	}

	//遍历方法
	public void Zlist(TreeNode2 node) {
		while (node != null) {
			// 找到最左边的结点
			while (node.leftType == 0) {
				node = node.left;
			}
			// 输出
			System.out.println(node);
			
			//开始向后继结点遍历
			while (node.rightType == 1) {
				node = node.right;
				System.out.println(node);
			}
			// 替换这个遍历的结点
			node = node.right;
		}

	}

前序线索化和遍历

	public TreeNode2 rootNode;
	public TreeNode2 preNode = null;
	public void QthreadNodes(TreeNode2 node) {
		if (node == null) {
			return;
		}

		if (node.left == null) {
			node.left = preNode;
			node.leftType = 1;
		}
		if (preNode != null && preNode.right == null) {
			preNode.right = node;
			preNode.rightType = 1;
		}

		preNode = node;

		if (node.leftType != 1)
			QthreadNodes(node.left);
		if (node.rightType != 1)
			QthreadNodes(node.right);
	}
	//遍历
	public void Qlist(TreeNode2 node) {
		 while(node!=null) {
			 while(node.leftType == 0) {
				 System.out.println(node);
				 node = node.left;
			 }
			 System.out.println(node);
			 node = node.right;
		 }
	}

后序线索化和遍历(结点增加父结点引用)

由于后序遍历比较难,附上完整代码:
一定要在构造结点的时候为父节点赋值!!
构建的树:
在这里插入图片描述

public class ThreadTree {

	public static void main(String[] args) {
		//创建结点
		TreeNode2 node1 = new TreeNode2(1, "");
		TreeNode2 node3 = new TreeNode2(3, "");
		TreeNode2 node6 = new TreeNode2(6, "");
		TreeNode2 node8 = new TreeNode2(8, "");
		TreeNode2 node10 = new TreeNode2(10, "");
		TreeNode2 node14 = new TreeNode2(14, "");
		
		node1.right = node6;
		node3.right = node10;
		node1.left = node3;
		node3.left = node8;
		node6.left = node14;
		
		//这一步很重要,没有这一步则遍历失败!!!
		node3.parentNode = node1;
		node6.parentNode = node1;
		node8.parentNode = node3;
		node10.parentNode = node3;
		node14.parentNode = node6;
		/////////////////////////
		
		Tree2 tree2 = new Tree2();
		tree2.HthreadNodes(node1);
		tree2.Hlist(node1);

	}
}

class Tree2 {
	public TreeNode2 rootNode;
	public TreeNode2 preNode = null;
	
	//后序线索化
	public void HthreadNodes(TreeNode2 node) {
		if (node == null) {
			return;
		}
		if (node.leftType != 1)
			HthreadNodes(node.left);
		if (node.rightType != 1)
			HthreadNodes(node.right);
		if (node.left == null) {
			node.left = preNode;
			node.leftType = 1;
		}
		if (preNode != null && preNode.right == null) {
			preNode.right = node;
			preNode.rightType = 1;
		}
		preNode = node;

	}
	
	//后续遍历,方法中不会初始化parentNode结点,请在调用前,手动初始化parentNode结点
	public void Hlist(TreeNode2 node) {
		if (node == null) {
			return;
		}
		// 先找到就开始遍历的结点,也就是最左边的结点(线索化之前原来树的最左边)
		//因为后序遍历就是从这里开始的
		// 迭代左指针,要过滤掉前驱的类型
		while (node != null && node.leftType == 0) {
			node = node.left;
		}
		while(node!=null) {
			//如果有后继结点,就可以顺着后继结点遍历了
			if(node.rightType == 1) {
				System.out.println(node);
				preNode = node;
				//记录上一个结点,向后继移动
				node = node.right;
			}else {
				//如果上次处理的结点时当前结点的右节点
				//因为后序的遍历顺序为左 -> 右 ->当前结点
				//所以可以处理当前结点了
				if(node.right == preNode) {
					System.out.println(node);
					
					//在后序遍历中,根节点时最后访问到的,如果是根节点
					//遍历结束
					if(node == rootNode) {
						return;
					}
					
					preNode = node;
					//回退到上一次结点
					node = node.parentNode;
					
				}else {
					//既没有后继结点,右边的结点也不是上次处理的结点
					node = node.right;
					while(node!=null&& node.leftType == 0) {
						node = node.left;
					}
				}
			}
		}

	}
}

//结点结构
class TreeNode2 {
	public int id;
	public String name;
	public TreeNode2 right;
	public TreeNode2 left;
	// leftType == 0表示指向的是左子树(默认),如果1则表示指向前驱节点
	// 如果rightType ==0表示指向是右子树(默认),如果1表示指向后继节点

	// 用于后续遍历
	public TreeNode2 parentNode;
	// 用于遍历
	public int leftType;
	public int rightType;

	public TreeNode2(int id, String name) {
		this.id = id;
		this.name = name;
	}

	@Override
	public String toString() {
		return "TreeNode2 [id=" + id + ", name=" + name + "]";
	}

}

发布了68 篇原创文章 · 获赞 12 · 访问量 5197

猜你喜欢

转载自blog.csdn.net/qq_40963076/article/details/105202438