• 《算法笔记》2. 链表、栈、队列、递归、哈希表、顺序表


    1 链表、栈、队列、递归、哈希

    1.1 链表

    1.1.1 单向链表

    单向链表的节点结构(可以实现成泛型) :

        public class Node {
            public int value;
            public Node next;
            
            public Node(int data) {
                value = data;
            }
            
        }
    

    1.1.2 双向链表

    双向链表的节点结构(可以实现成功泛型):

    	public static class DoubleNode {
    		public int value;
    		public DoubleNode last;
    		public DoubleNode next;
    
    		public DoubleNode(int data) {
    			value = data;
    		}
    	}
    

    1.1.3 单双链表简单练习

    1. 单链表和双链表如何反转

    1 -> 2 -> 3 转换为 3 -> 2 -> 1

    
    package class02;
    
    import java.util.ArrayList;
    
    public class Code01_ReverseList {
    
    	public static class Node {
    		public int value;
    		public Node next;
    
    		public Node(int data) {
    			value = data;
    		}
    	}
    
    	public static class DoubleNode {
    		public int value;
    		public DoubleNode last;
    		public DoubleNode next;
    
    		public DoubleNode(int data) {
    			value = data;
    		}
    	}
    
        // 翻转单向链表,传入头结点
    	public static Node reverseLinkedList(Node head) {
    		Node pre = null;
    		Node next = null;
    		while (head != null) {
    			next = head.next;
    			head.next = pre;
    			pre = head;
    			head = next;
    		}
    		return pre;
    	}
    
        // 翻转双向链表,传入头结点
    	public static DoubleNode reverseDoubleList(DoubleNode head) {
    		DoubleNode pre = null;
    		DoubleNode next = null;
    		while (head != null) {
    			next = head.next;
    			head.next = pre;
    			head.last = next;
    			pre = head;
    			head = next;
    		}
    		return pre;
    	}
    
    	public static Node testReverseLinkedList(Node head) {
    		if (head == null) {
    			return null;
    		}
    		ArrayList<Node> list = new ArrayList<>();
    		while (head != null) {
    			list.add(head);
    			head = head.next;
    		}
    		list.get(0).next = null;
    		int N = list.size();
    		for (int i = 1; i < N; i++) {
    			list.get(i).next = list.get(i - 1);
    		}
    		return list.get(N - 1);
    	}
    
    	public static DoubleNode testReverseDoubleList(DoubleNode head) {
    		if (head == null) {
    			return null;
    		}
    		ArrayList<DoubleNode> list = new ArrayList<>();
    		while (head != null) {
    			list.add(head);
    			head = head.next;
    		}
    		list.get(0).next = null;
    		DoubleNode pre = list.get(0);
    		int N = list.size();
    		for (int i = 1; i < N; i++) {
    			DoubleNode cur = list.get(i);
    			cur.last = null;
    			cur.next = pre;
    			pre.last = cur;
    			pre = cur;
    		}
    		return list.get(N - 1);
    	}
    
    	public static Node generateRandomLinkedList(int len, int value) {
    		int size = (int) (Math.random() * (len + 1));
    		if (size == 0) {
    			return null;
    		}
    		size--;
    		Node head = new Node((int) (Math.random() * (value + 1)));
    		Node pre = head;
    		while (size != 0) {
    			Node cur = new Node((int) (Math.random() * (value + 1)));
    			pre.next = cur;
    			pre = cur;
    			size--;
    		}
    		return head;
    	}
    
    	public static DoubleNode generateRandomDoubleList(int len, int value) {
    		int size = (int) (Math.random() * (len + 1));
    		if (size == 0) {
    			return null;
    		}
    		size--;
    		DoubleNode head = new DoubleNode((int) (Math.random() * (value + 1)));
    		DoubleNode pre = head;
    		while (size != 0) {
    			DoubleNode cur = new DoubleNode((int) (Math.random() * (value + 1)));
    			pre.next = cur;
    			cur.last = pre;
    			pre = cur;
    			size--;
    		}
    		return head;
    	}
    
    	// 要求无环,有环别用这个函数
    	public static boolean checkLinkedListEqual(Node head1, Node head2) {
    		while (head1 != null && head2 != null) {
    			if (head1.value != head2.value) {
    				return false;
    			}
    			head1 = head1.next;
    			head2 = head2.next;
    		}
    		return head1 == null && head2 == null;
    	}
    
    	// 要求无环,有环别用这个函数
    	public static boolean checkDoubleListEqual(DoubleNode head1, DoubleNode head2) {
    		boolean null1 = head1 == null;
    		boolean null2 = head2 == null;
    		if (null1 && null2) {
    			return true;
    		}
    		if (null1 ^ null2) {
    			return false;
    		}
    		if (head1.last != null || head2.last != null) {
    			return false;
    		}
    		DoubleNode end1 = null;
    		DoubleNode end2 = null;
    		while (head1 != null && head2 != null) {
    			if (head1.value != head2.value) {
    				return false;
    			}
    			end1 = head1;
    			end2 = head2;
    			head1 = head1.next;
    			head2 = head2.next;
    		}
    		if (head1 != null || head2 != null) {
    			return false;
    		}
    		while (end1 != null && end2 != null) {
    			if (end1.value != end2.value) {
    				return false;
    			}
    			end1 = end1.last;
    			end2 = end2.last;
    		}
    		return end1 == null && end2 == null;
    	}
    
    	public static void main(String[] args) {
    		int len = 50;
    		int value = 100;
    		int testTime = 100000;
    		for (int i = 0; i < testTime; i++) {
    			Node node1 = generateRandomLinkedList(len, value);
    			Node reverse1 = reverseLinkedList(node1);
    			Node back1 = testReverseLinkedList(reverse1);
    			if (!checkLinkedListEqual(node1, back1)) {
    				System.out.println("oops!");
    				break;
    			}
    			DoubleNode node2 = generateRandomDoubleList(len, value);
    			DoubleNode reverse2 = reverseDoubleList(node2);
    			DoubleNode back2 = testReverseDoubleList(reverse2);
    			if (!checkDoubleListEqual(node2, back2)) {
    				System.out.println("oops!");
    				break;
    			}
    		}
    		System.out.println("finish!");
    
    	}
    
    }
    
    
    1. 把给定的值都删除

    比如给定一个链表头结点,删除该节点上值为3的节点,那么可能头结点就是3,存在删头部的情况,这里最终返回应该是删除所有值为3的节点之后的新的头部

    
    package class02;
    
    public class Code02_DeleteGivenValue {
    
    	public static class Node {
    		public int value;
    		public Node next;
    
    		public Node(int data) {
    			this.value = data;
    		}
    	}
    
        // 先检查头部,寻找第一个不等于需要删除的值的节点,就是新的头部
    	public static Node removeValue(Node head, int num) {
    		while (head != null) {
    			if (head.value != num) {
    				break;
    			}
    			head = head.next;
    		}
    		// head来到 第一个不需要删的位置
    		Node pre = head;
    		Node cur = head;
    		// 
    		while (cur != null) {
    			if (cur.value == num) {
    				pre.next = cur.next;
    			} else {
    				pre = cur;
    			}
    			cur = cur.next;
    		}
    		return head;
    	}
    
    }
    
    

    Tips: Java中也有可能产生内存泄漏,与CPP不同,CPP的内存泄漏有可能是我们开辟了内存空间忘记释放。而Java的内存泄漏大可能是程序中的变量的生存周期引起的,如果该程序是一个类似定时任务的7*24小时不间断运行,那么申请的变量(数据结构)就有可能不会被及时释放。如果不注意往里面添加一些不必要的变量,这些变量就是内存泄漏

    1.2 栈、队列

    1. 逻辑概念

    栈:数据先进后出,犹如弹夹

    队列: 数据先进先出,排队

    1. 底层实现方式

    双向链表实现

    package class02;
    
    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Stack;
    
    public class Code03_DoubleEndsQueueToStackAndQueue {
    
    	public static class Node<T> {
    		public T value;
    		public Node<T> last;
    		public Node<T> next;
    
    		public Node(T data) {
    			value = data;
    		}
    	}
    
    	public static class DoubleEndsQueue<T> {
    		public Node<T> head;
    		public Node<T> tail;
    
            // 从头部加节点
    		public void addFromHead(T value) {
    			Node<T> cur = new Node<T>(value);
    			if (head == null) {
    				head = cur;
    				tail = cur;
    			} else {
    				cur.next = head;
    				head.last = cur;
    				head = cur;
    			}
    		}
    
            // 从尾部加节点
    		public void addFromBottom(T value) {
    			Node<T> cur = new Node<T>(value);
    			if (head == null) {
    				head = cur;
    				tail = cur;
    			} else {
    				cur.last = tail;
    				tail.next = cur;
    				tail = cur;
    			}
    		}
    
            // 从头部弹出节点
    		public T popFromHead() {
    			if (head == null) {
    				return null;
    			}
    			Node<T> cur = head;
    			if (head == tail) {
    				head = null;
    				tail = null;
    			} else {
    				head = head.next;
    				cur.next = null;
    				head.last = null;
    			}
    			return cur.value;
    		}
    
            // 从尾部弹出节点
    		public T popFromBottom() {
    			if (head == null) {
    				return null;
    			}
    			Node<T> cur = tail;
    			if (head == tail) {
    				head = null;
    				tail = null;
    			} else {
    				tail = tail.last;
    				tail.next = null;
    				cur.last = null;
    			}
    			return cur.value;
    		}
    
            // 该双向链表结构是否为空
    		public boolean isEmpty() {
    			return head == null;
    		}
    
    	}
    
        // 用上述双向链表结构实现栈
    	public static class MyStack<T> {
    		private DoubleEndsQueue<T> queue;
    
    		public MyStack() {
    			queue = new DoubleEndsQueue<T>();
    		}
    
    		public void push(T value) {
    			queue.addFromHead(value);
    		}
    
    		public T pop() {
    			return queue.popFromHead();
    		}
    
    		public boolean isEmpty() {
    			return queue.isEmpty();
    		}
    
    	}
    
        // 用上述双向链表结构实现队列
    	public static class MyQueue<T> {
    		private DoubleEndsQueue<T> queue;
    
    		public MyQueue() {
    			queue = new DoubleEndsQueue<T>();
    		}
    
    		public void push(T value) {
    			queue.addFromHead(value);
    		}
    
    		public T poll() {
    			return queue.popFromBottom();
    		}
    
    		public boolean isEmpty() {
    			return queue.isEmpty();
    		}
    
    	}
    
    	public static boolean isEqual(Integer o1, Integer o2) {
    		if (o1 == null && o2 != null) {
    			return false;
    		}
    		if (o1 != null && o2 == null) {
    			return false;
    		}
    		if (o1 == null && o2 == null) {
    			return true;
    		}
    		return o1.equals(o2);
    	}
    
    	public static void main(String[] args) {
    		int oneTestDataNum = 100;
    		int value = 10000;
    		int testTimes = 100000;
    		for (int i = 0; i < testTimes; i++) {
    			MyStack<Integer> myStack = new MyStack<>();
    			MyQueue<Integer> myQueue = new MyQueue<>();
    			Stack<Integer> stack = new Stack<>();
    			Queue<Integer> queue = new LinkedList<>();
    			for (int j = 0; j < oneTestDataNum; j++) {
    				int nums = (int) (Math.random() * value);
    				if (stack.isEmpty()) {
    					myStack.push(nums);
    					stack.push(nums);
    				} else {
    					if (Math.random() < 0.5) {
    						myStack.push(nums);
    						stack.push(nums);
    					} else {
    						if (!isEqual(myStack.pop(), stack.pop())) {
    							System.out.println("oops!");
    						}
    					}
    				}
    				int numq = (int) (Math.random() * value);
    				if (stack.isEmpty()) {
    					myQueue.push(numq);
    					queue.offer(numq);
    				} else {
    					if (Math.random() < 0.5) {
    						myQueue.push(numq);
    						queue.offer(numq);
    					} else {
    						if (!isEqual(myQueue.poll(), queue.poll())) {
    							System.out.println("oops!");
    						}
    					}
    				}
    			}
    		}
    		System.out.println("finish!");
    	}
    
    }
    
    

    数组实现,对于栈特别简单,对于队列,如下

    package class02;
    
    public class Code04_RingArray {
    
    	public static class MyQueue {
    	    // 数组结构
    		private int[] arr;
    		// 往当前队列添加数的下标位置
    		private int pushi;
    		// 当前队列需要出队列的位置
    		private int polli;
    		// 当前队列使用的空间大小
    		private int size;
    		// 数组最大大小,用户传入
    		private final int limit;
    
    		public MyQueue(int limit) {
    			arr = new int[limit];
    			pushi = 0;
    			polli = 0;
    			size = 0;
    			this.limit = limit;
    		}
    
    		public void push(int value) {
    			if (size == limit) {
    				throw new RuntimeException("栈满了,不能再加了");
    			}
    			size++;
    			arr[pushi] = value;
    			pushi = nextIndex(pushi);
    		}
    
    		public int pop() {
    			if (size == 0) {
    				throw new RuntimeException("栈空了,不能再拿了");
    			}
    			size--;
    			int ans = arr[polli];
    			polli = nextIndex(polli);
    			return ans;
    		}
    
    		public boolean isEmpty() {
    			return size == 0;
    		}
    
    		// 如果现在的下标是i,返回下一个位置,该实现可以实现环形的ringbuffer
    		private int nextIndex(int i) {
    			return i < limit - 1 ? i + 1 : 0;
    		}
    
    	}
    
    }
    

    1.3 栈、队列常见面试题

    一、实现一个特殊的栈,在基本功能的基础上,再实现返回栈中最小元素的功能更

    1、pop、push、getMin操作的时间复杂度都是O(1)

    2、设计的栈类型可以使用现成的栈结构

    思路:准备两个栈,一个data栈,一个min栈。数据压data栈,min栈对比min栈顶元素,谁小加谁。这样的话data栈和min栈是同步上升的,元素个数一样多,且min栈的栈顶,是data栈所有元素中最小的那个。数据弹出data栈,我们同步弹出min栈,保证个数相等,切min栈弹出的就是最小值

    package class02;
    
    import java.util.Stack;
    
    public class Code05_GetMinStack {
    
    	public static class MyStack1 {
    		private Stack<Integer> stackData;
    		private Stack<Integer> stackMin;
    
    		public MyStack1() {
    			this.stackData = new Stack<Integer>();
    			this.stackMin = new Stack<Integer>();
    		}
    
    		public void push(int newNum) {
    		    // 当前最小栈为空,直接压入
    			if (this.stackMin.isEmpty()) {
    				this.stackMin.push(newNum);
    			// 当前元素小于最小栈的栈顶,压入当前值
    			} else if (newNum <= this.getmin()) {
    				this.stackMin.push(newNum);
    			}
    			// 往数据栈中压入当前元素
    			this.stackData.push(newNum);
    		}
    
    		public int pop() {
    			if (this.stackData.isEmpty()) {
    				throw new RuntimeException("Your stack is empty.");
    			}
    			int value = this.stackData.pop();
    			if (value == this.getmin()) {
    				this.stackMin.pop();
    			}
    			return value;
    		}
    
    		public int getmin() {
    			if (this.stackMin.isEmpty()) {
    				throw new RuntimeException("Your stack is empty.");
    			}
    			return this.stackMin.peek();
    		}
    	}
    
    	public static class MyStack2 {
    		private Stack<Integer> stackData;
    		private Stack<Integer> stackMin;
    
    		public MyStack2() {
    			this.stackData = new Stack<Integer>();
    			this.stackMin = new Stack<Integer>();
    		}
    
    		public void push(int newNum) {
    			if (this.stackMin.isEmpty()) {
    				this.stackMin.push(newNum);
    			} else if (newNum < this.getmin()) {
    				this.stackMin.push(newNum);
    			} else {
    				int newMin = this.stackMin.peek();
    				this.stackMin.push(newMin);
    			}
    			this.stackData.push(newNum);
    		}
    
    		public int pop() {
    			if (this.stackData.isEmpty()) {
    				throw new RuntimeException("Your stack is empty.");
    			}
    			// 弹出操作,同步弹出,保证大小一致,只返回给用户data栈中的内容即可
    			this.stackMin.pop();
    			return this.stackData.pop();
    		}
    
    		public int getmin() {
    			if (this.stackMin.isEmpty()) {
    				throw new RuntimeException("Your stack is empty.");
    			}
    			return this.stackMin.peek();
    		}
    	}
    
    	public static void main(String[] args) {
    		MyStack1 stack1 = new MyStack1();
    		stack1.push(3);
    		System.out.println(stack1.getmin());
    		stack1.push(4);
    		System.out.println(stack1.getmin());
    		stack1.push(1);
    		System.out.println(stack1.getmin());
    		System.out.println(stack1.pop());
    		System.out.println(stack1.getmin());
    
    		System.out.println("=============");
    
    		MyStack1 stack2 = new MyStack1();
    		stack2.push(3);
    		System.out.println(stack2.getmin());
    		stack2.push(4);
    		System.out.println(stack2.getmin());
    		stack2.push(1);
    		System.out.println(stack2.getmin());
    		System.out.println(stack2.pop());
    		System.out.println(stack2.getmin());
    	}
    
    }
    

    二、如何用栈结构实现队列结构,如何用队列结构实现栈结构

    这两种结构的应用实在太多,刷题时会大量见到

    /**
    * 两个栈实现队列
    **/
    package class02;
    
    import java.util.Stack;
    
    public class Code06_TwoStacksImplementQueue {
    
    	public static class TwoStacksQueue {
    		public Stack<Integer> stackPush;
    		public Stack<Integer> stackPop;
    
    		public TwoStacksQueue() {
    			stackPush = new Stack<Integer>();
    			stackPop = new Stack<Integer>();
    		}
    
    		// push栈向pop栈倒入数据
    		private void pushToPop() {
    			if (stackPop.empty()) {
    				while (!stackPush.empty()) {
    					stackPop.push(stackPush.pop());
    				}
    			}
    		}
    
    		public void add(int pushInt) {
    			stackPush.push(pushInt);
    			pushToPop();
    		}
    
    		public int poll() {
    			if (stackPop.empty() && stackPush.empty()) {
    				throw new RuntimeException("Queue is empty!");
    			}
    			pushToPop();
    			return stackPop.pop();
    		}
    
    		public int peek() {
    			if (stackPop.empty() && stackPush.empty()) {
    				throw new RuntimeException("Queue is empty!");
    			}
    			pushToPop();
    			return stackPop.peek();
    		}
    	}
    
    	public static void main(String[] args) {
    		TwoStacksQueue test = new TwoStacksQueue();
    		test.add(1);
    		test.add(2);
    		test.add(3);
    		System.out.println(test.peek());
    		System.out.println(test.poll());
    		System.out.println(test.peek());
    		System.out.println(test.poll());
    		System.out.println(test.peek());
    		System.out.println(test.poll());
    	}
    
    }
    
    
    /**
    * 两个队列实现栈 
    **/
    package class02;
    
    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Stack;
    
    public class Code07_TwoQueueImplementStack {
    
    	public static class TwoQueueStack<T> {
    		public Queue<T> queue;
    		public Queue<T> help;
    
    		public TwoQueueStack() {
    			queue = new LinkedList<>();
    			help = new LinkedList<>();
    		}
    
    		public void push(T value) {
    			queue.offer(value);
    		}
    
    		public T poll() {
    			while (queue.size() > 1) {
    				help.offer(queue.poll());
    			}
    			T ans = queue.poll();
    			Queue<T> tmp = queue;
    			queue = help;
    			help = tmp;
    			return ans;
    		}
    
    		public T peek() {
    			while (queue.size() > 1) {
    				help.offer(queue.poll());
    			}
    			T ans = queue.poll();
    			help.offer(ans);
    			Queue<T> tmp = queue;
    			queue = help;
    			help = tmp;
    			return ans;
    		}
    
    		public boolean isEmpty() {
    			return queue.isEmpty();
    		}
    
    	}
    
    	public static void main(String[] args) {
    		System.out.println("test begin");
    		TwoQueueStack<Integer> myStack = new TwoQueueStack<>();
    		Stack<Integer> test = new Stack<>();
    		int testTime = 1000000;
    		int max = 1000000;
    		for (int i = 0; i < testTime; i++) {
    			if (myStack.isEmpty()) {
    				if (!test.isEmpty()) {
    					System.out.println("Oops");
    				}
    				int num = (int) (Math.random() * max);
    				myStack.push(num);
    				test.push(num);
    			} else {
    				if (Math.random() < 0.25) {
    					int num = (int) (Math.random() * max);
    					myStack.push(num);
    					test.push(num);
    				} else if (Math.random() < 0.5) {
    					if (!myStack.peek().equals(test.peek())) {
    						System.out.println("Oops");
    					}
    				} else if (Math.random() < 0.75) {
    					if (!myStack.poll().equals(test.pop())) {
    						System.out.println("Oops");
    					}
    				} else {
    					if (myStack.isEmpty() != test.isEmpty()) {
    						System.out.println("Oops");
    					}
    				}
    			}
    		}
    
    		System.out.println("test finish!");
    
    	}
    
    }
    

    1.4 递归

    1、从思想上理解递归

    2、从实现角度出发理解递归

    例子:

    求数组arr[L...R]中的最大值,怎么用递归方法实现

    1、 将[L...R]范围分成左右两半。左[L...Mid],右[Mid+1...R]
    2、 左部分求最大值,右部分求最大值
    3、[L...R]范围上的最大值,就是max{左部分最大值,右部分最大值}

    2步骤是个递归过程,当范围上只有一个数,就可以不用再递归了

    package class02;
    
    public class Code08_GetMax {
    
    	// 求arr中的最大值
    	public static int getMax(int[] arr) {
    		return process(arr, 0, arr.length - 1);
    	}
    
    	// arr[L..R]范围上求最大值  L ... R   N
    	public static int process(int[] arr, int L, int R) {
    		if (L == R) { // arr[L..R]范围上只有一个数,直接返回,base case
    			return arr[L];
    		}
    		int mid = L + ((R - L) >> 1); // 中点
    		// 左部分最大值
    		int leftMax = process(arr, L, mid);
    		// 右部分最大值
    		int rightMax = process(arr, mid + 1, R);
    		return Math.max(leftMax, rightMax);
    	}
    
    }
    

    递归在系统中是怎么实现的?递归实际上利用的是系统栈来实现的。保存当前调用现场,去执行子问题,子问题的返回作为现场的需要的参数填充,最终构建还原栈顶的现场,返回。所以递归行为不是玄学,任何递归都可以改为非递归实现,我们自己压栈用迭代等实现就行

    1.4.1 递归行为的时间复杂度

    对于满足

    T(N) = aT(N/b) + O(N^d)
    

    其中: a,b,d为常数

    公式表示,子问题的规模是一致的,该子问题调用了a次,N/b代表子问题的规模,O(N^d)为除去递归调用剩余的时间复杂度。

    比如上述问题的递归,[L...R]上有N个数,第一个子问题的规模是N/2,第二个子问题的规模也是N/2。子问题调用了2次。额为复杂度为O(1),那么公式为:

    T(N) = 2T(N/2) + O(N^0)
    

    结论:如果我们的递归满足这种公式,那么该递归的时间复杂度(Master公式)为

    logb^a > d   =>  O(N ^ (logb^a))
    
    logb^a < d   =>  O(N^d)
    
    logb^a == d   =>  O(N^d * logN)
    
    

    那么上述问题的a=2, b=2,d=0,满足第一条,递归时间复杂度为:O(N)

    1.5 哈希表HashMap、HashSet

    Hash表的增删改查,在使用的时候,一律认为时间复杂度是O(1)的

    在Java中,int double float基础类型,按值传递; Integer, Double, Float按引用传递的,比较包装类型的值是否相等,使用equals方法。

    注意:在Java底层,包装类如果范围比较小,底层仍然采用值传递,比如Integer如果范围在-128~127之间,是按值传递的

    但是在Hash表中,即使是包装类型的key,我们也一律按值传递,例如Hash<Integer,String>如果我们put相同的key的值,那么不会产生两个值相等的key而是覆盖操作。但是Hash表并不是一直是按值传递的,只是针对包装类型,如果是我们自定义的引用类型,那么仍然按引用传递

    1.6 顺序表 TreeMap、TreeSet

    顺序表比哈希表功能多,但是顺序表的很多操作时间复杂度是O(logN)

    有序表的底层可以有很多结构实现,比如AVL树,SB树,红黑树,跳表。其中AVL,SB,红黑都是具备各自平衡性的搜索二叉树

    由于平衡二叉树每时每刻都会维持自身的平衡,所以操作为O(logN)。暂时理解,后面会单独整理

    由于满足去重排序功能来维持底层树的平衡,所以如果是基础类型和包装类型的key直接按值来做比较,但是如果我们的key是自己定义的类型,那么我们要自己制定比较规则(比较器),用来让底层的树保持比较后的平衡

    package class02;
    
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.TreeMap;
    
    public class HashMapAndSortedMap {
    	
    	
    	public static class Node{
    		public int value;
    		public Node(int v) {
    			 value = v;
    		}
    	}
    	
    	public static void main(String[] args) {
    		// UnSortedMap
    		HashMap<Integer, String> map = new HashMap<>();
    		map.put(1000000, "我是1000000");
    		map.put(2, "我是2");
    		map.put(3, "我是3");
    		map.put(4, "我是4");
    		map.put(5, "我是5");
    		map.put(6, "我是6");
    		map.put(1000000, "我是1000001");
    		
    		System.out.println(map.containsKey(1));
    		System.out.println(map.containsKey(10));
    		
    		System.out.println(map.get(4));
    		System.out.println(map.get(10));
    		
    		map.put(4, "他是4");
    		System.out.println(map.get(4));
    		
    		map.remove(4);
    		System.out.println(map.get(4));
    		
    		
    		
    		//       key
    		HashSet<String>  set = new HashSet<>();
    		set.add("abc");
    		set.contains("abc");
    		set.remove("abc");
    		
    		// 哈希表,增、删、改、查,在使用时,O(1)
    		
    		
    		System.out.println("=====================");
    		
    		
    		int a = 100000;
    		int b = 100000;
    		System.out.println(a == b);
    		
    		Integer c = 100000;
    		Integer d = 100000;
    		System.out.println(c.equals(d));
    		
    		Integer e = 127;  //  - 128  ~  127
    		Integer f = 127;
    		System.out.println(e == f);
    		
    		
    		
    		HashMap<Node, String> map2 = new HashMap<>();
    		Node node1 = new Node(1);
    		Node node2 = node1;
    		map2.put(node1, "我是node1");
    		map2.put(node2, "我是node1");
    		System.out.println(map2.size());
    		
    		System.out.println("======================");
    		
    		TreeMap<Integer, String> treeMap = new TreeMap<>();
    		
    		treeMap.put(3, "我是3");
    		treeMap.put(4, "我是4");
    		treeMap.put(8, "我是8");
    		treeMap.put(5, "我是5");
    		treeMap.put(7, "我是7");
    		treeMap.put(1, "我是1");
    		treeMap.put(2, "我是2");
    
    		System.out.println(treeMap.containsKey(1));
    		System.out.println(treeMap.containsKey(10));
    		
    		System.out.println(treeMap.get(4));
    		System.out.println(treeMap.get(10));
    		
    		treeMap.put(4, "他是4");
    		System.out.println(treeMap.get(4));
    		
    		treeMap.remove(4);
    		System.out.println(treeMap.get(4));
    		
    		System.out.println(treeMap.firstKey());
    		System.out.println(treeMap.lastKey());
    		// <= 4
    		System.out.println(treeMap.floorKey(4));
    		// >= 4
    		System.out.println(treeMap.ceilingKey(4));
    		
    		// O(logN)	
    		
    	}
    	
    	
    	
    }
    
  • 相关阅读:
    jz2440 环境搭建遇到的问题
    Android开发10.2:UI组件AutoCompleteTextView(自动完成文本框)
    Android中RelativeLayout各个属性的含义
    【安装.net framework4.0】之安装失败,“安装时发生严重错误”
    【windows7 + Appium】之Appium安装以及其他工具安装配置
    【Selenium + Python】之OSError: [WinError 6] 句柄无效。
    【SoapUI、Postman、WebServiceStudio、Jmeter】接口测试工具结合测试webservice接口(发送XML格式参数)
    【Postman】接口测试工具:在谷歌浏览器安装插件方法以及使用说明
    【JMeter4.0学习(十一)】之JMeter对(Mysql、Oracle)数据库性能测试脚本开发
    【Selenium + Python】之 Excel、CSV、XML文件读取数据并运用数据百度查询
  • 原文地址:https://www.cnblogs.com/darope/p/13290810.html
Copyright © 2020-2023  润新知