文章目录
1.数据结构
1.1 线性表
定义接口
public interface List {
//返回线性表的大小,即数据元素的个数。
public int getSize();
//如果线性表为空返回true,否则返回false。
public boolean isEmpty();
//判断线性表是否包含数据元素e
public boolean contains(Object e);
//返回数据元素e在线性表中的序号
public int indexOf(Object e);
//将数据元素e插入到线性表中i号位置
public void insert(int i, Object e) throws OutOfBoundaryException;
//将数据元素e插入到元素obj之前
public boolean insertBefore(Object obj, Object e);
//将数据元素e插入到元素obj之后
public boolean insertAfter(Object obj, Object e);
//删除线性表中序号为i的元素,并返回之
public Object remove(int i) throws OutOfBoundaryException;
//删除线性表中第一个与e相同的元素
public boolean remove(Object e);
//替换线性表中序号为i的数据元素为e,返回原数据元素
public Object replace(int i, Object e) throws OutOfBoundaryException;
//返回线性表中序号为i的数据元素
public Object get(int i) throws OutOfBoundaryException;
}
1.1.1线性表的顺序存储与实现
public class SequentialList implements List {
//private final int LEN = 8; //数组的默认大小
private int size; //线性表中数据元素的个数
private Object[] elements; //数据元素数组
//构造方法
public SequentialList(int maxSize){
size = 0;
elements = new Object[maxSize];
}
//返回线性表的大小,即数据元素的个数。
public int getSize(){
return size;
}
//如果线性表为空返回true,否则返回false。
public boolean isEmpty(){
return size == 0;
}
//判断线性表是否包含数据元素e
public boolean contains(Object e){
for(int i = 0; i < size; i++){
if(Objects.equals(e,elements[i]))
return true;
}
return false;
}
//返回数据元素e在线性表中的序号
public int indexOf(Object e){
for(int i = 0; i < size; i++){
if(Objects.equals(e,elements[i]))
return i;
}
return -1;
}
//将数据元素e插入到线性表中i号位置
public void insert(int i, Object e) throws OutOfBoundaryException {
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
if(size >= elements.length)
//expandSpace();
throw new OutOfBoundaryException("顺序表已满");
for(int j = size; j > i; j--)
elements[j] = elements[j-1];
elements[i] = e;
size++;
}
// private void expandSpace(){
// Object[] a = new Object[elements.length*2];
// for(int i = 0; i < elements.length; i++)
// a[i] = elements[i];
// elements = a;
// }
//将数据元素e插入到元素obj之前
public boolean insertBefore(Object obj, Object e){
int i = indexOf(obj);
if(i < 0)
return false;
insert(i,e);
return true;
}
//将数据元素e插入到元素obj之后
public boolean insertAfter(Object obj, Object e){
int i = indexOf(obj);
if(i < 0)
return false;
insert(i+1,e);
return true;
}
//删除线性表中序号为i的元素,并返回之
public Object remove(int i) throws OutOfBoundaryException{
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
Object obj = elements[i];
for(int j = i; j < size - 1; j++)
elements[j] = elements[j+1];
elements[--size] = null;
return obj;
}
//删除线性表中第一个与e相同的元素
public boolean remove(Object e){
int i = indexOf(e);
if(i < 0)
return false;
remove(i);
return true;
}
//替换线性表中序号为i的数据元素为e,返回原数据元素
public Object replace(int i,Object e) throws OutOfBoundaryException{
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
Object obj = elements[i];
elements[i] = e;
return obj;
}
//返回线性表中序号为i的数据元素
public Object get(int i) throws OutOfBoundaryException{
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
return elements[i];
}
//测试
public void print(){
for(int i = 0; i < size; i++){
System.out.print(get(i)+" ");
}
System.out.println();
}
public static void main(String[] args) {
SequentialList list = new SequentialList(10);
list.insert(0,"a");
list.insert(1,"b");
list.insert(2,"c");
list.insert(3,"d");
list.insert(4,"e");
list.print();
list.remove(3);
list.print();
}
}
定义异常类
public class OutOfBoundaryException extends RuntimeException{
public OutOfBoundaryException(String err){
super(err);
}
}
1.1.1线性表的链式存储与实现
定义单链表结点
public class SLNode{
private Object data;
private SLNode next;
public SLNode(){
this(null,null);
}
public SLNode(Object data){
this(data,null);
}
public SLNode(Object data, SLNode next){
this.data = data;
this.next = next;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public SLNode getNext() {
return next;
}
public void setNext(SLNode next) {
this.next = next;
}
}
public class LinkedList implements List {
private SLNode head; //单链表头结点引用
private int size;
public LinkedList(){
size = 0;
head = new SLNode();
}
// 辅助方法:获取数据元素e所在结点的前驱结点
private SLNode getPreNode(Object e){
SLNode p = head;
while(p.getNext() != null){
if(Objects.equals(p.getNext().getData(),e))
return p;
else
p = p.getNext();
}
return null;
}
// 辅助方法:获取序号为0<=i<size的元素所在结点的前驱结点
private SLNode getPreNode(int i){
SLNode p = head;
for(; i > 0; i--)
p = p.getNext();
return p;
}
//获取序号为0<=i<size的元素所在结点
private SLNode getNode(int i){
SLNode p = head.getNext();
for(; i > 0; i--)
p = p.getNext();
return p;
}
//返回线性表的大小,即数据元素的个数。
public int getSize(){
return size;
}
//如果线性表为空返回true,否则返回false。
public boolean isEmpty(){
return size == 0;
}
//判断线性表是否包含数据元素e
public boolean contains(Object e){
SLNode p = head.getNext();
while(p != null){
if(Objects.equals(p.getData(),e))
return true;
else
p = p.getNext();
}
return false;
}
//返回数据元素e在线性表中的序号
public int indexOf(Object e){
SLNode p = head.getNext();
int index = 0;
while(p != null){
if(Objects.equals(p.getData(),e))
return index;
else{
index++;
p = p.getNext();
}
}
return -1;
}
//将数据元素e插入到线性表中i号位置
public void insert(int i, Object e) throws OutOfBoundaryException {
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
SLNode p = getPreNode(i);
SLNode q = new SLNode(e,p.getNext());
p.setNext(q);
size++;
}
//将数据元素e插入到元素obj之前
public boolean insertBefore(Object obj, Object e){
SLNode p = getPreNode(obj);
if(p != null){
SLNode q = new SLNode(e,p.getNext());
p.setNext(q);
size++;
return true;
}
return false;
}
//将数据元素e插入到元素obj之后
public boolean insertAfter(Object obj, Object e){
SLNode p = head.getNext();
while(p != null){
if(Objects.equals(p.getData(),obj)){
SLNode q = new SLNode(e,p.getNext());
p.setNext(q);
size++;
return true;
}
else p = p.getNext();
}
return false;
}
//默认尾部添加元素
public void add(Object obj) { insert(getSize(),obj);}
//删除线性表中序号为i的元素,并返回之
public Object remove(int i) throws OutOfBoundaryException{
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
SLNode p = getPreNode(i);
Object obj = p.getNext().getData();
p.setNext(p.getNext().getNext());
size--;
return obj;
}
//删除线性表中第一个与e相同的元素
public boolean remove(Object e){
SLNode p = getPreNode(e);
if(p != null){
p.setNext(p.getNext().getNext());
size--;
return true;
}
return false;
}
//替换线性表中序号为i的数据元素为e,返回原数据元素
public Object replace(int i,Object e) throws OutOfBoundaryException{
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
SLNode p = getNode(i);
Object obj = p.getData();
p.setData(e);
return obj;
}
//返回线性表中序号为i的数据元素
public Object get(int i) throws OutOfBoundaryException{
if(i < 0 || i > size)
throw new OutOfBoundaryException("错误,指定的插入序号越界。");
SLNode p = getNode(i);
return p.getData();
}
//测试
//打印输出所有结点
public void print() {
SLNode node = head.getNext();
while (node != null) {
System.out.print(node.getData() + " ");
node = node.getNext();
}
System.out.println();
}
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
list.print();
System.out.println(list.indexOf("ccc"));
System.out.println(list.get(3));
System.out.println(list.remove(2));
list.print();
list.insert(1,"abc");
list.print();
list.replace(1,"abcd");
list.print();
}
}
1.2 栈
定义接口
public interface Stack {
//返回堆栈的大小
public int getSize();
//判断堆栈是否为空
public boolean isEmpty();
//数据元素e入栈
public void push(Object e);
//栈顶元素出栈
public Object pop() throws StackEmptyException;
//取栈顶元素,但不出栈
public Object peek() throws StackEmptyException;
}
1.2.1 栈的顺序存储实现
public class SequentialStack implements Stack {
private final int LEN = 8; //数组的默认大小
private Object[] elements; //数据元素数组
private int top; //栈顶指针
public SequentialStack(){
top = -1; //top=-1表示空栈
elements = new Object[LEN];
}
//返回堆栈的大小
public int getSize(){
return top + 1;
}
//判断堆栈是否为空
public boolean isEmpty(){
return top < 0;
}
//数据元素e入栈
public void push(Object e){
if(getSize() >= elements.length)
expandSpace();
elements[++top] = e;
}
//数组扩容
private void expandSpace(){
Object[] a = new Object[elements.length*2];
for(int i = 0; i < elements.length; i++)
a[i] = elements[i];
elements = a;
}
//栈顶元素出栈
public Object pop() throws StackEmptyException{
if(getSize() < 1)
throw new StackEmptyException("错误,堆栈为空");
Object obj = elements[top];
elements[top--] = null;
return obj;
}
//取栈顶元素,但不出栈
public Object peek() throws StackEmptyException{
if(getSize() < 1)
throw new StackEmptyException("错误,堆栈为空");
return elements[top];
}
//测试
public void print(){
for(int i = top; i >= 0; i--){
System.out.print(elements[i]+" ");
}
System.out.println();
}
public static void main(String[] args) {
SequentialStack stack = new SequentialStack();
stack.push("aaa");
stack.push("bbb");
stack.push("ccc");
stack.push("ddd");
stack.push("eee");
stack.print();
System.out.println(stack.peek());
System.out.println(stack.pop());
System.out.println(stack.peek());
stack.print();
System.out.println(stack.getSize());
}
}
1.2.2 栈的链式存储实现
public class LinkedStack implements Stack {
private SLNode top; //链表首结点引用
private int size;
public LinkedStack(){
top = null;
size = 0;
}
//返回堆栈的大小
public int getSize(){
return size;
}
//判断堆栈是否为空
public boolean isEmpty(){
return size == 0;
}
//数据元素e入栈
public void push(Object e){
SLNode q = new SLNode(e,top);
top = q;
size++;
}
//栈顶元素出栈
public Object pop() throws StackEmptyException {
if(size < 1)
throw new StackEmptyException("错误,堆栈为空");
Object obj = top.getData();
top = top.getNext();
size--;
return obj;
}
//取栈顶元素,但不出栈
public Object peek() throws StackEmptyException{
if(size < 1)
throw new StackEmptyException("错误,堆栈为空");
return top.getData();
}
//测试
public void print(){
SLNode p = top;
while(p != null){
System.out.print(p.getData() + " ");
p = p.getNext();
}
System.out.println();
}
public static void main(String[] args) {
LinkedStack stack = new LinkedStack();
stack.push("aaa");
stack.push("bbb");
stack.push("ccc");
stack.push("ddd");
stack.push("eee");
stack.print();
System.out.println(stack.peek());
System.out.println(stack.pop());
System.out.println(stack.peek());
stack.print();
System.out.println(stack.getSize());
}
}
1.3 队列
定义接口
public interface Queue {
//返回队列的大小
public int getSize();
//判断队列是否为空
public boolean isEmpty();
//数据元素e入队
public void enqueue(Object e);
//队首元素出队
public Object dequeue() throws QueueException;
//取队首元素,但不出队
public Object peek() throws QueueException;
}
1.3.1 队列的顺序存储实现
如果队列当作一般的表用数组加以实现,则在删除元素时需要将其余元素都向前移动一个位置,需要O(n)时间,为提高效率,逻辑上采用循环数组实现循环队列。
入队时 队尾指针rear逆时针移动一位
出队时 队首指着front逆时针移动一位
解决区分满队列和空队列的方式有两种
①少使用一个存储空间,当队尾指针的下一单元就是队首指针所指单元时,则停止入队。(本次采用此方案)
②增设size标志 size=0则队空 size=capacity则队满
public class SequentialQueue implements Queue {
private Object[] elements;
private int capacity; //数组的大小
private int front; //队首指针,指向队首
private int rear; //队尾指针,指向队尾后一个位置
public SequentialQueue(){
this(10);
}
public SequentialQueue(int cap){
this.capacity = cap + 1;
elements = new Object[capacity];
front = rear = 0;
}
//返回队列的大小
public int getSize(){
return (rear - front + capacity) % capacity;
}
//判断队列是否为空
public boolean isEmpty(){
return front == rear;
}
//数据元素e入队
public void enqueue(Object e) throws QueueException{
if(getSize() == capacity - 1)
throw new QueueException("错误:队列已满");
elements[rear] = e;
rear = (rear + 1) % capacity;
}
//队首元素出队
public Object dequeue() throws QueueException{
if(isEmpty())
throw new QueueException("错误:队列为空");
Object obj = elements[front];
elements[front] = null;
front = (front + 1) % capacity;
return obj;
}
//取队首元素,但不出队
public Object peek() throws QueueException{
if(isEmpty())
throw new QueueException("错误:队列为空");
return elements[front];
}
//测试
public static void main(String[] args) {
SequentialQueue queue = new SequentialQueue(6) ;
queue.enqueue("A");
queue.enqueue("B");
queue.enqueue("C");
queue.enqueue("D");
queue.enqueue("E");
queue.enqueue("F");
System.out.println(queue.getSize());
System.out.println(queue.peek());
System.out.println(queue.dequeue());
System.out.println(queue.peek());
}
}
定义异常类
public class QueueException extends RuntimeException{
public QueueException(String err){
super(err);
}
}
1.3.2 队列的链式存储实现
public class LinkedQueue implements Queue{
private SLNode front;
private SLNode rear;
private int size;
public LinkedQueue(){
front = new SLNode();
rear = front;
size = 0;
}
//返回队列的大小
public int getSize(){
return size;
}
//判断队列是否为空
public boolean isEmpty(){
return size == 0;
}
//数据元素e入队
public void enqueue(Object e){
SLNode p = new SLNode(e);
rear.setNext(p);
rear = p;
size++;
}
//队首元素出队
public Object dequeue() throws QueueException{
if(size < 1)
throw new QueueException("错误:队列为空");
SLNode p = front.getNext();
front.setNext(p.getNext());
size--;
if(size < 1)
rear = front; //如果队列为空,rear指向头结点
return p.getData();
}
//取队首元素,但不出队
public Object peek() throws QueueException{
if(size < 1)
throw new QueueException("错误:队列为空");
return front.getNext().getData();
}
//测试
public static void main(String[] args) {
SequentialQueue queue = new SequentialQueue() ;
queue.enqueue("A");
queue.enqueue("B");
queue.enqueue("C");
queue.enqueue("D");
queue.enqueue("E");
queue.enqueue("F");
System.out.println(queue.getSize());
System.out.println(queue.peek());
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
System.out.println(queue.peek());
}
}
1.4 树
定义二叉树的节点类
public class BinTreeNode {
private Object data; //数据域
private BinTreeNode lChild; //左孩子
private BinTreeNode rChild; //右孩子
public BinTreeNode() {this(null); }
public BinTreeNode(Object data) {
this.data = data;
}
public BinTreeNode(Object data, BinTreeNode lChild, BinTreeNode rChild) {
this.data = data;
this.lChild = lChild;
this.rChild = rChild;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public BinTreeNode getlChild() {
return lChild;
}
public void setlChild(BinTreeNode lChild) {
this.lChild = lChild;
}
public BinTreeNode getrChild() {
return rChild;
}
public void setrChild(BinTreeNode rChild) {
this.rChild = rChild;
}
}
public class BinTree {
private BinTreeNode root; //根节点
public BinTree() { root = null; }
public BinTree(BinTreeNode root) { this.root = root; }
public BinTreeNode getRoot() { return root; }
public void setRoot(BinTreeNode root) { this.root = root; }
/**
* 由先根遍历序列和中根遍历序列 创建一个二叉树
* 先序遍历序列中,第一个结点必为根节点;中序遍历序列被根节点分为两部分:根结点之前的部分为左子树结点中序序列,根结点之后的为右子树结点中序序列
* 设中序序列根节点之前的部分的长度i,则先序序列根节点之后的i个结点属于左子树(第一个为左子树根结点)
* preIndex表示先序序列开始位置,inIndex表示中序序列开始位置,count表示序列长度
*/
public BinTree(String preOrder, String inOrder, int preIndex, int inIndex, int count){
if (count > 0) {
char r = preOrder.charAt(preIndex); //取先根遍历序列中的第一个结点作为根结点
int i = 0;
for (; i < count; i++) {
if (r == inOrder.charAt(i + inIndex)) { //寻找根结点在中根遍历序列中的位置
break;
}
}
root = new BinTreeNode(r);
root.setlChild(new BinTree(preOrder, inOrder, preIndex+1, inIndex, i).root);
root.setrChild(new BinTree(preOrder, inOrder, preIndex+i+1, inIndex+i+1, count-i-1).root);
}
}
//先序遍历二叉树
public void preOrder(){
preOrderRecursion(this.root);
System.out.println();
preOrderTraverse(this.root);
System.out.println();
preOrderTraverse1(this.root);
System.out.println();
}
//先序遍历的递归算法
private void preOrderRecursion(BinTreeNode rt){
if(rt == null) return; //递归基,空树直接返回
System.out.print(rt.getData()); //访问根结点
preOrderRecursion(rt.getlChild()); //遍历左子树
preOrderRecursion(rt.getrChild()); //遍历右子树
}
/**先序遍历的非递归算法1
*1.沿着根节点p一直向左走,沿途访问经过的根节点,并将这些根节点的非空右子树入栈,直到p为空
*2.取出栈顶结点,在外层循环中继续先序遍历这棵以p指向的子树。
*3.如果堆栈为空,则表示再没有右子树需要遍历,此时结束外层循环,完成整棵树的先序遍历
*/
private void preOrderTraverse(BinTreeNode rt){
if(rt == null) return;
BinTreeNode p = rt;
Stack s = new LinkedStack();
while(p != null){
while(p != null){ //向左走到尽头
System.out.print(p.getData()); //访问根
if(p.getrChild() != null)
s.push(p.getrChild()); //右子树根节点入栈
p = p.getlChild();
}
if(!s.isEmpty())
p = (BinTreeNode)s.pop(); //右子树根退栈遍历右子树
}
}
/**先序遍历的非递归算法2
*1.沿着根节点p一直向左走,沿途访问根节点,将根节点入栈,直到p为空
*2.取出上一层根节点,然后转向该根节点的右子树进行先序遍历
*3.如果堆栈和p都为空,则表示没有更多的子树需要遍历,此时结束外层循环,完成整棵树的遍历
*/
private void preOrderTraverse1(BinTreeNode rt){
if(rt == null) return;
BinTreeNode p = rt;
Stack s = new LinkedStack();
while(p != null || !s.isEmpty()){
while(p != null){ //向左走到尽头
System.out.print(p.getData()); //访问根
s.push(p); //根节点入栈
p = p.getlChild();
}
p = (BinTreeNode)s.pop(); //根退栈遍历右子树
p = p.getrChild();
}
}
//中序遍历二叉树
public void inOrder(){
inOrderRecursion(this.root);
System.out.println();
inOrderTraverse(this.root);
System.out.println();
}
//中序遍历的递归算法
private void inOrderRecursion(BinTreeNode rt){
if(rt == null) return; //递归基,空树直接返回
inOrderRecursion(rt.getlChild()); //遍历左子树
System.out.print(rt.getData()); //访问根结点
inOrderRecursion(rt.getrChild()); //遍历右子树
}
/**中序遍历的非递归算法
*1.沿着根节点p一直向左走,沿途将根节点入栈,直到p为空
*2.取出上一层根节点访问之,然后转向该根节点的右子树进行中序遍历
*3.如果堆栈和p都为空,则表示没有更多的子树需要遍历,此时结束外层循环,完成整棵树的遍历
*/
private void inOrderTraverse(BinTreeNode rt) {
if (rt == null) return;
BinTreeNode p = rt;
Stack s = new LinkedStack();
while(p != null || !s.isEmpty()){
while(p != null){ //一直往左走
s.push(p); //将根节点入栈
p = p.getlChild();
}
p = (BinTreeNode)s.pop(); //取出栈顶根节点访问之
System.out.print(p.getData());
p = p.getrChild(); //转向根的右子树进行遍历
}
}
//后序遍历二叉树
public void postOrder(){
postOrderRecursion(this.root);
System.out.println();
postOrderTraverse(this.root);
System.out.println();
}
//后序遍历的递归算法
private void postOrderRecursion(BinTreeNode rt){
if(rt == null) return; //递归基,空树直接返回
postOrderRecursion(rt.getlChild()); //遍历左子树
postOrderRecursion(rt.getrChild()); //遍历右子树
System.out.print(rt.getData()); //访问根结点
}
/**后序遍历的非递归算法
*1.第一个while循环中,沿着根节点p先向左子树深入,如果左子树为空,则向右子树深入,沿途将根节点入栈,直到p为空
*2.第一个if语句说明应该取出栈顶根节点访问,此时栈顶结点为叶子或无右子树的单分支结点
*3.访问p之后,说明以p为根的子树访问完毕,判断p是否为其父节点的右孩子(当前栈顶即为其父节点),如果是,
* 则说明只要访问其父亲就可以完成对以p的父亲为根的子树的遍历;如果不是,则转向其父节点的右子树继续后序遍历
*4.如果堆栈和p都为空,则表示没有更多的子树需要遍历,此时结束外层循环,完成整棵树的遍历
*/
private void postOrderTraverse(BinTreeNode rt) {
if (rt == null) return;
BinTreeNode p = rt;
Stack s = new LinkedStack();
while (p != null || !s.isEmpty()) {
while(p != null){ //先左后右不断深入
s.push(p); //将根节点入栈
if(p.getlChild() != null)
p = p.getlChild();
else
p = p.getrChild();
}
if(!s.isEmpty()){
p = (BinTreeNode)s.pop(); //取出栈顶根节点访问之
System.out.print(p.getData());
}
//满足条件时,说明栈顶根节点右子树已访问,应出栈访问之
while(!s.isEmpty() && ((BinTreeNode)s.peek()).getrChild() == p){
p = (BinTreeNode)s.pop();
System.out.print(p.getData());
}
//转向栈顶根节点的右子树继续后序遍历
if(!s.isEmpty())
p = ((BinTreeNode)s.peek()).getrChild();
else
p = null;
}
}
//层次遍历二叉树
public void levelOrder(){
levelOrderTraverse(this.root);
System.out.println();
}
//使用队列完成二叉树的按层遍历
private void levelOrderTraverse(BinTreeNode rt) {
if(rt == null) return;
Queue q = new SequentialQueue();
q.enqueue(rt); //根节点入队
while(!q.isEmpty()){
BinTreeNode p = (BinTreeNode)q.dequeue(); //取出队首结点p并访问
System.out.print(p.getData());
if(p.getlChild() != null) //将p的非空左右孩子依次入队
q.enqueue(p.getlChild());
if(p.getrChild() != null)
q.enqueue(p.getrChild());
}
}
//在树中查找元素e,返回其所在结点
public BinTreeNode find(Object e){
return searchE(root,e);
}
//递归查找元素e
private BinTreeNode searchE(BinTreeNode rt,Object e){
if(rt == null) return null;
if(Objects.equals(rt.getData(),e)) return rt; //如果是根节点,返回根
BinTreeNode v = searchE(rt.getlChild(),e); //否则在左子树中找
if(v == null) v = searchE(rt.getrChild(),e); //没找到,在右子树中找
return v;
}
//统计结点数,基于先根遍历
public int countNode(BinTreeNode rt){
if(rt != null)
return countNode(rt.getlChild())+countNode(rt.getrChild())+1;
else
return 0;
}
/**
* 二叉树的深度
* 基于后根遍历
* 先求出左子树的深度,在求出右子树的深度,二者较大的一方+1就是二叉树深度
*/
public int getDepth(BinTreeNode rt) {
if (rt != null) {
int lDepth = getDepth(rt.getlChild()); //遍历左子树
int rDepth = getDepth(rt.getrChild()); //遍历右子树
return (lDepth > rDepth ? lDepth : rDepth) + 1; //访问根结点
}
return 0;
}
//测试
public static void main(String[] args) {
/*----------------------
A
B C
D E F
G H
-----------------------*/
//创建一个二叉树(由叶子结点往上依次添加,最后是根结点)
BinTreeNode d = new BinTreeNode('D');
BinTreeNode g = new BinTreeNode('G');
BinTreeNode h = new BinTreeNode('H');
BinTreeNode e = new BinTreeNode('E', g, null);
BinTreeNode b = new BinTreeNode('B', d, e);
BinTreeNode f = new BinTreeNode('F', null, h);
BinTreeNode c = new BinTreeNode('C', f, null);
BinTreeNode a = new BinTreeNode('A', b, c);
BinTree tree = new BinTree(a);
BinTreeNode root = tree.getRoot();
System.out.print("先根遍历:");
System.out.println();
tree.preOrder();
System.out.print("中根遍历:");
System.out.println();
tree.inOrder();
System.out.print("后根遍历:");
System.out.println();
tree.postOrder();
System.out.print("层次遍历:");
System.out.println();
tree.levelOrder();
System.out.println(tree.countNode(root));
System.out.println(tree.getDepth(root));
String preOrder = "ABDEGCFH";
String inOrder = "DBGEAFHC";
BinTree rt = new BinTree(preOrder,inOrder,0,0,preOrder.length());
System.out.print("后根遍历:");
System.out.println();
rt.postOrder();
}
}
1.5 图
1.5.1 邻接矩阵存储结构
enum GraphKind {
UDG, //无向图
DG, //有向图
}
//图的顶点类
class Vertex {
public char lable;
public boolean wasvisited;
public Vertex(char lab){
lable = lab;
wasvisited = false;
}
}
public class AGraph {
private GraphKind kind; //图的种类标志
private final int MAX_VERTS = 20; //顶点最大个数
private Vertex[] vertexList; //顶点数组
private int adjMat[][]; //邻接矩阵
public int nVerts; //顶点数
public AGraph(){
this(GraphKind.UDG);
}
public AGraph(GraphKind kind) {
this.kind = kind;
vertexList = new Vertex[MAX_VERTS];
adjMat = new int[MAX_VERTS][MAX_VERTS];
nVerts = 0;
for (int i = 0; i < MAX_VERTS; i++) {
for (int j = 0; j < MAX_VERTS; j++) {
adjMat[i][j] = 0;
}
}
}
//加入顶点
public void addVertex(char lab){
vertexList[nVerts ++] = new Vertex(lab);
}
//加入边
public void addEdage(int start,int end){
switch (kind) {
case UDG:
adjMat[start][end] = 1;
adjMat[end][start] = 1;
break;
case DG:
adjMat[start][end] = 1;
break;
}
}
//取得未被访问过的邻接点
public int getAdjUnvisitedVertex(int v){
for(int i = 0; i < nVerts; i++){
if (adjMat[v][i] == 1 && vertexList[i].wasvisited == false){
return i;
}
}
return -1;
}
//重置顶点状态
public void resetVexStatus(){
for (int i = 0; i < nVerts; i++){
vertexList[i].wasvisited = false;
}
}
/**
* 深度优先遍历算法(非递归实现)
* 从图的某一点x出发,访问任意一个与之相邻且未被访问到的点y,然后访问任意一个与y相邻且未被访问到的点z,…直到所有点都被访问过为止。
* 1.构造一个栈存放顶点,访问第一个顶点v1,并将v1入栈
* 2.将v1的未被访问的邻接点入栈并访问,若没有未曾访问的邻接点则出栈
* 3.重复2直到栈空
*/
public void DFS(int n){
Stack s = new LinkedStack();
vertexList[n].wasvisited = true;
System.out.print(vertexList[n].lable+" ");
s.push(n);
while (!s.isEmpty()){
int v = getAdjUnvisitedVertex((int)s.peek()); //找到栈当前顶点邻接且未被访问的顶点
if(v == -1){ //如果当前顶点值为-1,则表示没有邻接且未被访问顶点,顶点出栈
s.pop();
} else { //否则访问下一个邻接顶点
vertexList[v].wasvisited = true;
System.out.print(vertexList[v].lable+" ");
s.push(v);
}
}
}
public void DFSTraverse(){
for (int i = 0; i < nVerts; i++){
if(vertexList[i].wasvisited == false){ //确保有向图时每个顶点都被访问到
DFS(i);
}
}
}
/**
* 广度优先遍历算法(非递归实现)
* 从图的某一点x出发,访问所有与之相邻且未被访问到的点y,z,l,m...,然后分别从这些邻接点出发依次访问它们的所有邻接点,直到所有点都被访问过为止。
* 1.构造一个队列存放顶点,访问第一个顶点v1,并将v1入队
* 2.v1出队,将v1的所有未被访问的邻接点入队并访问
* 3.重复2直到队空
*/
public void BFS(int n){
Queue q = new SequentialQueue();
vertexList[n].wasvisited = true;
System.out.print(vertexList[n].lable+" ");
q.enqueue(n);
int v2;
while (!q.isEmpty()){
int v1 = (int)q.dequeue();
while((v2 = getAdjUnvisitedVertex(v1)) != -1) {
vertexList[v2].wasvisited = true;
System.out.print(vertexList[v2].lable+" ");
q.enqueue(v2);
}
}
}
public void BFSTraverse(){
for (int i = 0; i < nVerts; i++){
if(vertexList[i].wasvisited == false){ //确保有向图时每个顶点都被访问到
BFS(i);
}
}
}
//测试
public static void main(String[] args) {
AGraph aGraph = new AGraph(GraphKind.UDG);
aGraph.addVertex('a');
aGraph.addVertex('b');
aGraph.addVertex('c');
aGraph.addVertex('d');
aGraph.addVertex('e');
aGraph.addVertex('f');
aGraph.addEdage(0,1);
aGraph.addEdage(0,3);
aGraph.addEdage(0,4);
aGraph.addEdage(1,2);
aGraph.addEdage(1,4);
aGraph.addEdage(2,4);
aGraph.addEdage(2,5);
aGraph.addEdage(4,5);
System.out.println("\n无向图深度优先:");
aGraph.DFSTraverse();
aGraph.resetVexStatus();
System.out.println("\n无向图广度优先:");
aGraph.BFSTraverse();
AGraph aGraph1 = new AGraph(GraphKind.DG);
aGraph1.addVertex('a');
aGraph1.addVertex('b');
aGraph1.addVertex('c');
aGraph1.addVertex('d');
aGraph1.addVertex('e');
aGraph1.addVertex('f');
aGraph1.addEdage(0,1);
aGraph1.addEdage(0,4);
aGraph1.addEdage(3,0);
aGraph1.addEdage(2,1);
aGraph1.addEdage(1,4);
aGraph1.addEdage(4,2);
aGraph1.addEdage(5,2);
aGraph1.addEdage(4,5);
System.out.println("\n有向图深度优先:");
aGraph1.DFSTraverse();
aGraph1.resetVexStatus();
System.out.println("\n有向图广度优先:");
aGraph1.BFSTraverse();
}
}
1.5.2 邻接表存储结构
enum mGraphKind {
UDG, //无向图
DG, //有向图
}
public class MGraph<E> {
// 邻接表中表对应的链表的顶点
private class ENode<E>{
private int adjvex; // 邻接顶点序号
private int weight; //弧的权值
private ENode nextadj; //下一个邻接表结点
ENode(int adjvex, int weight){
this.adjvex = adjvex;
this.weight = weight;
}
}
// 邻接表中表的顶点
private class VNode<E>{
private E name; //顶点名称
private ENode firstadj; //链接表的第一个结点
}
private mGraphKind kind; //图的种类标志
private VNode<E>[] vexs; // 顶点数组
private final int MAX_VERTS = 20; //顶点最大个数
private int numOfVexs; // 顶点的实际数量
private boolean[] visited;// 判断顶点是否被访问过
public MGraph(){
this(mGraphKind.UDG);
}
MGraph(mGraphKind kind){
this.kind = kind;
vexs = (VNode<E>[]) Array.newInstance(VNode.class, MAX_VERTS);
}
//添加顶点
public void insertVertex(E vertexName){
if (numOfVexs >= MAX_VERTS)
System.out.println("超出最大个数");
VNode<E> vex = new VNode<E>();
vex.name = vertexName;
vexs[numOfVexs++] = vex;
}
//添加边
public void insertEdge(int v1, int v2, int weight){
if (v1 < 0 || v2 < 0 || v1 >= numOfVexs || v2 >= numOfVexs){
throw new ArrayIndexOutOfBoundsException();
}
ENode vex1 = new ENode(v2, weight);
// 索引为index1的顶点没有邻接顶点
if (vexs[v1].firstadj == null) {
vexs[v1].firstadj = vex1;
}
// 索引为index1的顶点有邻接顶点
else { //链表插入操作
vex1.nextadj = vexs[v1].firstadj;
vexs[v1].firstadj = vex1;
}
switch (kind) {
case UDG:
ENode vex2 = new ENode(v1, weight);
// 索引为index2的顶点没有邻接顶点
if (vexs[v2].firstadj == null) {
vexs[v2].firstadj = vex2;
}
// 索引为index2的顶点有邻接顶点
else {
vex2.nextadj = vexs[v2].firstadj;
vexs[v2].firstadj = vex2;
}
break;
case DG:
break;
}
}
//重置顶点状态
public void resetVexStatus(){
for (int i = 0; i < numOfVexs; i++){
visited[i] = false;
}
}
/**
* 深度优先遍历算法(非递归实现)
* 从图的某一点x出发,访问任意一个与之相邻且未被访问到的点y,然后访问任意一个与y相邻且未被访问到的点z,…直到所有点都被访问过为止。
* 1.构造一个栈存放顶点,访问第一个顶点v1,并将v1入栈
* 2.v1出栈,将v1的所有未被访问的邻接点入栈并访问
* 3.重复2直到栈空
*/
public void depthFirstSearch(int v) {
if (v < 0 || v >= numOfVexs)
throw new ArrayIndexOutOfBoundsException();
Stack stack = new LinkedStack();
stack.push(v);
visited[v] = true;
ENode current;
while (!stack.isEmpty()) {
v = (int)stack.pop();
System.out.print(vexs[v].name + " ");
current = vexs[v].firstadj;
while (current != null) {
if (!visited[current.adjvex]) {
stack.push(current.adjvex);
visited[current.adjvex] = true;
}
current = current.nextadj;
}
}
}
public void DFSTraverse(){
visited = new boolean[numOfVexs];
for (int i = 0; i < numOfVexs; i++){
if(visited[i] == false){ //确保有向图时每个顶点都被访问到
depthFirstSearch(i);
}
}
}
/**
* 广度优先遍历算法(非递归实现)
* 从图的某一点x出发,访问所有与之相邻且未被访问到的点y,z,l,m...,然后分别从这些邻接点出发依次访问它们的所有邻接点,直到所有点都被访问过为止。
* 1.构造一个队列存放顶点,访问第一个顶点v1,并将v1入队
* 2.v1出队,将v1的所有未被访问的邻接点入队并访问
* 3.重复2直到队空
*/
public void breadFirstSearch(int v) {
if (v < 0 || v >= numOfVexs)
throw new ArrayIndexOutOfBoundsException();
Queue queue = new SequentialQueue();
queue.enqueue(v);
visited[v] = true;
ENode current;
while (!queue.isEmpty()) {
v = (int)queue.dequeue();
System.out.print(vexs[v].name + " ");
current = vexs[v].firstadj;
while (current != null) {
if (!visited[current.adjvex]) {
queue.enqueue(current.adjvex);
visited[current.adjvex] = true;
}
current = current.nextadj;
}
}
}
public void BFSTraverse(){
visited = new boolean[numOfVexs];
for (int i = 0; i < numOfVexs; i++){
if(visited[i] == false){ //确保有向图时每个顶点都被访问到
breadFirstSearch(i);
}
}
}
//测试
public static void main(String[] args) {
MGraph mGraph = new MGraph(mGraphKind.DG);
mGraph.insertVertex("V0");
mGraph.insertVertex("V1");
mGraph.insertVertex("V2");
mGraph.insertVertex("V3");
mGraph.insertVertex("V4");
mGraph.insertEdge(0,4,6);
mGraph.insertEdge(1,0,9);
mGraph.insertEdge(1,2,3);
mGraph.insertEdge(2,0,2);
mGraph.insertEdge(2,3,5);
mGraph.insertEdge(3,4,1);
System.out.println("\n有向图深度优先:");
mGraph.DFSTraverse();
mGraph.resetVexStatus();
System.out.println("\n有向图广度优先:");
mGraph.BFSTraverse();
MGraph mGraph1 = new MGraph(mGraphKind.UDG);
mGraph1.insertVertex("V0");
mGraph1.insertVertex("V1");
mGraph1.insertVertex("V2");
mGraph1.insertVertex("V3");
mGraph1.insertVertex("V4");
mGraph1.insertEdge(0,4,6);
mGraph1.insertEdge(1,0,9);
mGraph1.insertEdge(1,2,3);
mGraph1.insertEdge(2,0,2);
mGraph1.insertEdge(2,3,5);
mGraph1.insertEdge(3,4,1);
System.out.println("\n无向图深度优先:");
mGraph1.DFSTraverse();
mGraph1.resetVexStatus();
System.out.println("\n无向图广度优先:");
mGraph1.BFSTraverse();
}
}
2.算法
2.1 查找
2.1.1 顺序查找与二分法查找
public class LinearSearch {
/**
* 顺序查找
* 时间复杂度 最坏:O(n) 最好:O(1) 平均:O(n)
* 空间复杂度 O(1)
*/
public static int seqSearch(int[] arr, int key) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == key) {
return i;
}
}
return -1;
}
/**
* 二分法查找(折半查找)
* 条件:顺序存储的有序表(从小到大)
* 时间复杂度 最坏:O(log n) 最好:O(1) 平均:O(log n)
* 空间复杂度 O(1)
*/
public static int binSearch(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = (low + high) / 2; //中间位置,当前比较的数据元素位置
if (arr[mid] == key) {
return mid;
} else if (arr[mid] > key) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
/**
* 测试
*/
public static void main(String[] args) {
int[] arr = {7,5,2,3,4,9,1,0,2};
System.out.println(LinearSearch.seqSearch(arr, 9));
int[] brr = {1,2,3,4,5,6,7,8,9};
System.out.println(LinearSearch.binSearch(brr, 7));
}
}
2.1.2 哈希表
定义结点
public class Node<K,V> {
int hash;
K key;
V value;
Node next;
}
实现哈希表:数组+链表,哈希函数暂时采用最简单的取余法
public class MyHashMap<K,V> {
Node[] table;
int size;
public MyHashMap() {
table = new Node[16];
}
public int size() {
return this.size;
}
public boolean isEmpty() {
return size == 0;
}
public V get(K key) {
int hash = myHash(key.hashCode(),table.length);
V value = null;
if (table[hash] != null) {
Node temp = table[hash];
while (temp != null) {
if (temp.key.equals(key)) {
value = (V)temp.value;
break;
} else {
temp = temp.next;
}
}
}
return value;
}
public void put(K key, V value) {
//todo:数组扩容
Node newNode = new Node();
newNode.hash = myHash(key.hashCode(),table.length);
newNode.key = key;
newNode.value = value;
Node temp = table[newNode.hash];
boolean keyRepeat = false;
Node lastNode = null;
//此处数组元素为空,则直接将节点放入
if (temp == null) {
table[newNode.hash] = newNode;
size ++;
} else {
while (temp != null) {
//key重复则覆盖
if (temp.key.equals(key)) {
temp.value = value;
keyRepeat = true;
break;
} else {//不重复则遍历下一个
lastNode = temp;
temp = temp.next;
}
}
if(!keyRepeat){ //如果没有重复,则添加至链表最后
lastNode.next = newNode;
size ++;
}
}
}
public int myHash(int v, int length){
return v & (length - 1);
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer("{");
//遍历数组
for (int i = 0; i < table.length; i++) {
Node temp = table[i];
while (temp != null) { //遍历链表
stringBuffer.append(temp.key + ":" + temp.value + ",");
temp = temp.next;
}
}
stringBuffer.setCharAt(stringBuffer.length()-1,'}');
return stringBuffer.toString();
}
/**
* 测试
*/
public static void main(String[] args){
MyHashMap<Integer,String> m1 = new MyHashMap<>();
m1.put(10,"aa");
m1.put(20,"bb");
m1.put(30,"cc");
m1.put(20,"ssss");
m1.put(26,"aa1");
m1.put(42,"aa2");
System.out.println(m1);
System.out.println(m1.get(30));
System.out.println(m1.size);
}
}
2.1.3 二叉查找树(二叉排序树)
定义:
(1) 若左子树不为空,则左子树上所有结点的值均不大于根结点的值
(2) 若右子树不为空,则右子树上所有节点的值均不小于根结点的值
(3) 左右子树也都是二叉排序树
时间复杂度: 查找插入删除都是 O(h) h是树的高度,h∈[log n,n]
定义二叉树的结点类
public class BinTreeNode {
private Integer data; //数据域
private BinTreeNode lChild; //左孩子
private BinTreeNode rChild; //右孩子
public BinTreeNode() { }
public BinTreeNode(Integer data) {
this.data = data;
}
public BinTreeNode(Integer data, BinTreeNode lChild, BinTreeNode rChild) {
this.data = data;
this.lChild = lChild;
this.rChild = rChild;
}
public Integer getData() {
return data;
}
public void setData(Integer data) {
this.data = data;
}
public BinTreeNode getlChild() {
return lChild;
}
public void setlChild(BinTreeNode lChild) {
this.lChild = lChild;
}
public BinTreeNode getrChild() {
return rChild;
}
public void setrChild(BinTreeNode rChild) {
this.rChild = rChild;
}
}
实现二叉排序树
public class BSTree {
public BinTreeNode root;
public BSTree() {
}
public BSTree(BinTreeNode root) {
this.root = root;
}
//中序遍历二叉树
public void inOrder(){
inOrderRecursion(this.root);
System.out.println();
}
//中序遍历的递归算法
private void inOrderRecursion(BinTreeNode rt){
if(rt == null) return; //递归基,空树直接返回
inOrderRecursion(rt.getlChild()); //遍历左子树
System.out.print(rt.getData()+" "); //访问根结点
inOrderRecursion(rt.getrChild()); //遍历右子树
}
/**
* 二叉排序树查找算法 递归实现
* 查找树为空 或 k=结点域值,返回该结点
* k<结点域值,则在左子树查找
* k>结点域值,则在右子树查找
*/
private BinTreeNode binTSearchRecursion(BinTreeNode rt, Integer key) {
if (rt == null || rt.getData() == key) { //等于
return rt;
}
if (key < rt.getData()) {
return binTSearchRecursion(rt.getlChild(),key); //小于
}
return binTSearchRecursion(rt.getrChild(),key); //大于
}
/**
* 二叉排序树查找算法 非递归实现
*/
private BinTreeNode binTSearch(BinTreeNode rt, Integer key) {
while (rt != null && rt.getData() != key) {
if (key < rt.getData()) {
rt = rt.getlChild();
} else {
rt = rt.getrChild();
}
}
return rt;
}
//二叉查找树中最小元素:从根节点向左不断深入,直到到达最左的叶子结点
private BinTreeNode minBST(BinTreeNode rt) {
if(rt != null){
while (rt.getlChild() != null) {
rt = rt.getlChild();
}
}
return rt;
}
//二叉查找树中最大元素:从根节点向右不断深入,直到到达最右的叶子结点
private BinTreeNode maxBST(BinTreeNode rt) {
if(rt != null){
while (rt.getrChild() != null) {
rt = rt.getrChild();
}
}
return rt;
}
/**
* 二叉排序树插入算法
* 从根节点逐层深入,若新节点关键字小,向左子树深入,否则向右子树深入;
* 直到向某个结点的左子树深入而其左子树为空,或向某个结点的右子树深入而其右子树为空时,则确定了新节点的插入位置
*/
private void insertBST(Integer key) {
BinTreeNode p = null;
BinTreeNode current = root;
while (current != null) { //找到待插入位置
p = current;
if (key < current.getData()) {
current = current.getlChild();
} else {
current = current.getrChild();
}
}
if (p == null) {
root = new BinTreeNode(key); //树为空
} else if (key < p.getData()) {
p.setlChild(new BinTreeNode(key));
} else {
p.setrChild(new BinTreeNode(key));
}
}
/**
* 二叉排序树删除算法
* case1:结点T只有左子树或只有右子树或为叶子结点,当结点是左孩子时,只要令子树为其双亲结点的左子树即可;否则,令其子树为其双亲结点的右子树即可(T为叶子结点的情况也包含进去了)。
* case2: 结点T既有左子树又有右子树,先用中序序列中结点T的前驱或后继替换T,然后删除其前驱或后继结点即可,此时的T的前驱或后继结点必然是没有右孩子或左孩子的结点。
*/
private Integer removeBST(Integer key) {
if (root == null || key == null) {
return null;
}
return removeBST(root, key, null);
}
private Integer removeBST(BinTreeNode T, Integer key, BinTreeNode parent) {
if (T == null) {
return null;
}
if (key < T.getData()) {
return removeBST(T.getlChild(), key, T);
}
if (key > T.getData()) {
return removeBST(T.getrChild(), key, T);
}
//key==T.data时
//左右子树都不为空
if (T.getlChild() != null && T.getrChild() != null) {
BinTreeNode next = T.getrChild(); //找到待删除结点T的后继结点
while (next.getlChild() != null) {
next = next.getlChild();
}
int tmp = T.getData(); //交换T与后继结点的值
T.setData(next.getData());
next.setData(tmp);
return removeBST(T.getrChild(), key, T); //删除后继结点(此时后继结点必然没有左孩子)
}
//T是根结点 度为1
if (parent == null) {
if (T.getlChild() != null) {
root = T.getlChild();
} else {
root = T.getrChild();
}
return T.getData();
}
//判断T是左子结点还是右子结点
if (T == parent.getlChild()) {
if (T.getlChild() != null) {
parent.setlChild(T.getlChild());
} else {
parent.setlChild(T.getrChild());
}
} else {
if (T.getlChild() != null) {
parent.setrChild(T.getlChild());
} else {
parent.setrChild(T.getrChild());
}
}
return T.getData();
}
/**
* 测试
*/
public static void main(String[] args) {
/*----------------------
49
12 65
11 35 50
15 60
-----------------------*/
//创建一个二叉树(由叶子结点往上依次添加,最后是根结点)
BinTreeNode N11 = new BinTreeNode(11);
BinTreeNode N15 = new BinTreeNode(15);
BinTreeNode N60 = new BinTreeNode(60);
BinTreeNode N35 = new BinTreeNode(35, N15, null);
BinTreeNode N12 = new BinTreeNode(12, N11, N35);
BinTreeNode N50 = new BinTreeNode(50, null, N60);
BinTreeNode N65 = new BinTreeNode(65, N50, null);
BinTreeNode N49 = new BinTreeNode(49, N12, N65);
BSTree tree = new BSTree(N49);
BinTreeNode root = tree.root;
System.out.print("中根遍历:");
tree.inOrder();
System.out.println(tree.binTSearch(root,20));
tree.insertBST(25);
System.out.println(tree.removeBST(49));
System.out.print("中根遍历:");
tree.inOrder();
System.out.println("创建一个空树:");
tree = new BSTree();
for (int i = 1; i <= 10; i++) {
tree.insertBST(i);
}
System.out.print("中根遍历:");
tree.inOrder();
}
}
2.2 排序
public class MySort {
//插入类排序:基本思想是逐个考察每个待排序元素,将每个新元素插入到前面已经排好序的序列中适当的位置上,使得新序列仍然是一个有序序列
/**
* 直接插入排序:从第二个元素开始直到第n个元素,逐个向有序序列中执行插入操作
* 一般来说在含有j-1个元素的有序序列中插入一个元素的方法是:从第j-1个元素开始依次向前搜索应当插入的位置,同时向后右移元素
* 时间复杂度:O(n2)
* 空间复杂度:O(1)
* 稳定的排序算法
*/
public static int[] insertSort(int[] arr) {
if (arr.length == 0) {
return arr;
}
int i, j ,temp;
for (i = 1; i < arr.length; i++) {
temp = arr[i]; //暂存待插入数据
for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
arr[j + 1] = arr[j]; //将前面比arr[i]大的数据向后移动
}
arr[j + 1] = temp; //插入到j+1的位置
}
return arr;
}
/**
* 希尔排序:将待排序的元素分为多个子序列,对各个子序列分别进行直接插入排序
* 选择一个增量序列;按增量序列个数k,对待排序元素序列进行k趟排序;每趟排序,根据对应的增量d,将待排序列分割成d个子序列,分别对各子序列进行直接插入排序
* 时间复杂度:(目前尚未彻底解决)
* 空间复杂度:O(1)
* 不稳定的排序算法
*/
public static int[] shellSort(int[] arr, int[] d) { //d[]为增量数组 (最后一个增量值必须是1)
if (arr.length == 0) {
return arr;
}
int i, j, temp;
for (int k = 0; k < d.length; k++) { //k趟排序,k为步长序列长度
int dk = d[k];
for (i = dk; i < arr.length; i++) { //直接插入排序算法
temp = arr[i];
for (j = i - dk; j >= 0 && arr[j] > temp; j -= dk) {
arr[j + dk] = arr[j];
}
arr[j + dk] = temp;
}
}
return arr;
}
//交换类排序:通过两两比较待排元素的关键字,若发现与排序要求相逆,则交换之。
/**
* 冒泡排序
* 第1趟:将n个元素的第一个和第二个进行比较,如果两个元素位置为逆序,则交换两个元素;进而比较第二个和第三个元素,直至比较第n-1个元素和第n个元素。经过上述过程将关键字最大的元素放到了最后一个位置
* 第2-n趟:以此类推,对前n-i+1个元素进行比较,使得前n-i+1个元素中关键字最大的元素被放到第n-i+1个位置
* 时间复杂度:O(n2)
* 空间复杂度:O(1)
* 稳定的排序算法
*/
public static int[] bubbleSort(int[] arr) {
if (arr.length == 0) {
return arr;
}
int i, j;
for (i = 1; i < arr.length; i++) {
for (j = 0; j < arr.length-i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
/**
* 快速排序:通过基准数x将序列一分为二,左子序列元素均小于x,右子序列元素均大于x;递归的对左右子序列排序
* 时间复杂度:平均:O(n log n), 最坏为O(n2)
* 时间复杂度分析:一次划分算法从两头交替搜索,直到low与high重合,因此时间复杂度是O(n)
* 理想情况,每次划分所选择的中间数恰好将当前序列几乎等分,经过log n趟划分便可得到长度为1的子表;最坏的情况,每次所选的中间数是当前序列的最大或最小元素,使得每次划分所得子表中一个为空表,另一个为原表的长度-1,这样需要经过n趟划分
* 空间复杂度:O(log n), 最坏为O(n) (需要栈空间实现递归)
* 不稳定的排序算法
*/
public static int[] quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivotLoc = partition(arr, low, high);
quickSort(arr, low, pivotLoc-1); //低子表递归排序
quickSort(arr, pivotLoc+1, high); //高字表递归排序
}
return arr;
}
//一趟快速排序
private static int partition(int[] arr, int i, int j) {
int pivot = arr[i]; //暂存基准数据
while (i != j) {
while (i < j && arr[j] >= pivot)
j--; //从后遍历查找小于基准的数据
arr[i] = arr[j];
while (i < j && arr[i] <= pivot)
i++; //从前遍历查找大于基准的数据
arr[j] = arr[i];
}
arr[i] = pivot;
return i;
}
//选择类排序:每一趟从n-i+1个元素中选取一个关键字最小的元素作为有序序列中第i个元素
/**
* 简单选择排序:第k趟,从第k个元素开始的n-k+1个元素中选出关键字最小的元素与第k个元素交换
* 时间复杂度:O(n2)
* 空间复杂度:O(1)
* 不稳定的排序算法
*/
public static int[] selectSort(int[] arr) {
if (arr.length == 0) {
return arr;
}
for (int i = 0; i < arr.length; i++) {
int min = i;
for (int j = i+1; j < arr.length; j++) {
if (arr[j] < arr[i])
min = j;
}
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
return arr;
}
/**
* 堆排序
* 1、将待排序的序列构造成一个大顶堆(完全二叉树,每个节点的值都大于或等于其子节点的值),根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
* 2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
* 3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,得到一个有序的序列了。
*
* 构建大顶堆:找到所有非叶子节点(叶子节点可以看作已符合堆要求的节点),调整它们的父子关系,从下往上遍历
* 时间复杂度:O(n log n)
* 空间复杂度:O(1)
* 不稳定的排序算法
*/
public static int[] heapSort(int[] arr) {
if (arr == null || arr.length == 0) {
return arr;
}
int len = arr.length;
// 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组
for (int i = (int)Math.floor(len / 2) - 1; i >= 0; i--) { //完全二叉树,第一个非叶子节点的索引为arr.length/2-1。
heapAdjust(arr, i, len);
}
// 交换堆顶和当前末尾的节点,重置大顶堆
for (int i = len - 1; i > 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapAdjust(arr, 0, i);
}
return arr;
}
private static void heapAdjust(int[] arr, int i, int len) {
// 先根据堆性质,找出当前节点左右子节点的索引
int left = 2 * i + 1;
int right = 2 * i + 2;
// 默认当前节点(父节点)是最大值。
int largestIndex = i;
if (left < len && arr[left] > arr[largestIndex]) {
// 如果有左节点,并且左节点的值更大,更新最大值的索引
largestIndex = left;
}
if (right < len && arr[right] > arr[largestIndex]) {
// 如果有右节点,并且右节点的值更大,更新最大值的索引
largestIndex = right;
}
if (largestIndex != i) {
// 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换
int temp = arr[largestIndex];
arr[largestIndex] = arr[i];
arr[i] = temp;
// 因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
heapAdjust(arr, largestIndex, len);
}
}
/**
* 归并排序:将待排序的序列划分为大小相等(或大致相等)的两个子序列;递归划分直至子序列规模为1;将两个有序的子序列合并为一个有序序列
* 时间复杂度:O(n log n)
* 空间复杂度:O(n)
* 稳定的排序算法
*/
public static int[] mergeSort(int[] arr) {
sort(arr, 0, arr.length - 1);
return arr;
}
//将arr序列的[L R]序列排序
public static void sort(int[] arr, int L, int R) {
if(L == R) {
return;
}
int mid = L + ((R - L) >> 1);
sort(arr, L, mid); //左子序列排序
sort(arr, mid + 1, R); //右子序列排序
merge(arr, L, mid, R); //合并两段已排好序的序列成一个排序数组
}
//将两段排序好的数组结合成一个排序数组
private static void merge(int[] arr, int L, int mid, int R) {
int[] temp = new int[R-L+1];
int i = 0;
int p1 = L;
int p2 = mid + 1;
// 比较左右两部分的元素,哪个小,把那个元素填入temp中
while(p1 <= mid && p2 <= R) {
temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
// 上面的循环退出后,把剩余的元素依次填入到temp中
// 以下两个while只有一个会执行
while(p1 <= mid) {
temp[i++] = arr[p1++];
}
while(p2 <= R) {
temp[i++] = arr[p2++];
}
// 把最终的排序的结果复制给原数组
for(i = 0; i < temp.length; i++) {
arr[L + i] = temp[i];
}
}
/**
* 测试
*/
public static void main(String[] args) {
int[] arr = {26,53,48,11,13,48,32,15};
int[] brr = MySort.insertSort(arr);
System.out.println("直接插入排序 "+Arrays.toString(brr));
int[] d = {5, 3, 1}; //增量序列
brr = MySort.shellSort(arr, d);
System.out.println("希尔排序 "+Arrays.toString(brr));
brr = MySort.bubbleSort(arr);
System.out.println("冒泡排序 "+Arrays.toString(brr));
brr = MySort.quickSort(arr, 0, arr.length-1);
System.out.println("快速排序 "+Arrays.toString(brr));
brr = MySort.selectSort(arr);
System.out.println("简单选择排序 "+Arrays.toString(brr));
brr = MySort.heapSort(arr);
System.out.println("堆排序 "+Arrays.toString(brr));
brr = MySort.mergeSort(arr);
System.out.println("归并排序 "+Arrays.toString(brr));
}
}