二分法_链表_栈和队列_哈希表和有序表_2


计数排序,基数排序,桶排序,选择排序,冒泡排序,插入排序,希尔排序,归并排序,快速排序
的基础算法思想和代码

二分法

时间复杂度O(logN)

针对于有序数组:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。

基本思想:

  1. 假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较,

  2. 如果当前位置arr[k]值等于key,则查找成功;

  3. 若key小于当前位置值arr[k],则在数列的前半段中查找,arr[low,mid-1];前半段继续从中间位置比较;

  4. 若key大于当前位置值arr[k],则在数列的后半段中继续查找arr[mid+1,high],

  5. 直到找到为止,时间复杂度:O(log(n))


  1. 在一个有序数组中,查找某个数是否存在
public class Exist {
    public static void main(String[] args) {
        int arr[] = {1,2,3,5,6,9};
        System.out.println(exist(arr, 9));
    }
    public static boolean exist(int[] sortArr,int num){
        if (sortArr == null && sortArr.length == 0){
            return false;
        }
        int L = 0;
        int R = sortArr.length - 1;
        int mid = 0;
        while (L<R){
            //下面语句相当于mid = (L+R)/2
            mid = L + ((R - L) >> 1);
            if (sortArr[mid] == num){
                return true;
            }else if(sortArr[mid] > num){
                R = mid - 1;
            }else {
                L = mid + 1;
            }
        }
        return sortArr[L] == num;
    }
}
  1. 求大于某个数所在位置的索引(如果相同,取最左边的数)
/**
 * @Author ZhouT
 * @Date 2021/3/23 18:51
 * 求大于某个数所在位置的索引(如果相同,取最左边的数)
 */
public class NearNumIndex {
    public static void main(String[] args) {
        int[] arr = {1,5,6,9,8,10,99};
        int index = nearNumIndex(arr, 9);
        System.out.println(index);
    }

    protected static int nearNumIndex(int[] arr, int value) {
        int L = 0;
        int R = arr.length - 1;
        int index = -1;
        while (L <= R) {
            int mid = L + ((R - L) >> 1);
            if (arr[mid] >= value) {
                index = mid;
                R = mid - 1;
            } else {
                L = mid + 1;
            }
        }
        return index;
    }
}

异或

1.交换两个数
public class Swap {
    public static void main(String[] args) {
        int[] arr = {1,2};
        swap1(arr ,arr[1],arr[0]);
        System.out.println( Arrays.toString(arr));
    }
    //第一种方法:
    static void swap1(int[] arr, int i, int j) {
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }
    //第二种方法
    static void swap2(int[] arr, int i, int minPos) {
        int temp = arr[i];
        arr[i] = arr[minPos];
        arr[minPos] = temp;
    }
}
2.一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这个奇数次并打印这个数
public class SelectNum {
    public static void printOddTimesNum(int[] arr){
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];
        }
        System.out.println(eor);
    }

}

链表

1.单向链表的反转

/**
 * 单链表
 */
public static class Node{
    public int value;
    public Node next;

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

    public static Node reverseList(Node head){
        Node pre = null;
        Node next = null;
        while (head!=null){
            next = head.next;

            head.next = pre;
            pre = head;

            head = next;
        }
        return pre;
    }

2.双向链表的反转

/**
 * 双向链表
 */
public static class DoubleNode{
    public int value;
    public DoubleNode last;
    public DoubleNode next;

    public DoubleNode(int data){
        value = data;
    }
}

    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;
    }

3.删除单向链表中的某个数

/**
 * @Author Z
 * @Date 2021/3/24 13:35
 * 删除链表中的某个数(可能有多个相同的数)
 */
public class DeleteNumList {

    public static class Node {
        public int value;
        public Node next;

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

    public static Node removeValue(Node head, int num) {
        //处理如果链表头是要删除的数
        while (head != null) {
            if (head.value != num) {
                break;
            }
            head = head.next;
        }
        
        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;
    }
}

栈和队列

时间复杂度O(1)

队列先进先出

栈先进后出

1.使用双向链表实现栈和队列

/**
 * @Author ZhouT
 * @Date 2021/3/24 14:33
 * 使用双向链表实现栈和队列
 */
public class 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!");
    }

}

2.使用数组实现栈和队列

即怎么用数组实现不超过固定大小的队列和栈?

栈:一个栈,使用数组,加东西使index++,弹出东西使index–

队列:

令pollIndex=0;pushIndex=0;

增加一个变量size = 0;

limit为这个队列的最大限制;

如下:

public static class MyQueue {
		private int[] arr;
		private int pushi;
		private int polli;
		private int size;
		private final int limit;
		
	public MyQueue(int l) {
		arr = new int[l];
		pushi = 0;
		polli = 0;
		size = 0;
		limit = l;
	}

	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(pulli);
		return ans;
	}

	public boolean isEmpty() {
		return size == 0;
	}

	private int nextIndex(int i) {
		return i < limit - 1 ? i + 1 : 0;
	}

}

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

方法1:

思想:时间复杂度O(1)

  1. 准备两个栈Data栈和Min栈,就可以实现了

  2. 第一次push时把这个数同时push进Data栈和Min栈;

  3. 把第二次push的数放入Data栈,和Min栈的栈顶元素进行比较,

  4. 如果这个数比Min栈栈顶元素大,则把Min栈栈顶元素push到Min栈中(这时,Min栈有两个相同的数);

  5. 如果这个数比Min栈栈顶元素小,则把这个数push到Min栈中,这个数就成为了新的栈顶;

  6. 总而言之,每次push时,就是把这个数push到Data数组中,同时和Min栈的栈顶元素进行比较,如果这个数比Min栈栈顶元素小,则把这个数push进去,如果栈顶元素更小,把栈顶元素再次push进去;

  7. 当push完成后,Min栈栈顶元素就是最小的数了。

  8. 当发生pop操作,把Data栈和Min栈的栈顶元素同时弹出。

  9. 如何实现getMin呢?就是弹出Min栈的栈顶元素。

//算法思想:
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);
            }
/**
 * @Author Z
 * @Date 2021/3/24 16:45
 * 实现一个特殊的栈,在基本功能的基础上,再实现返回栈中最小元素的功能
 */
public class 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.");
            }
            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());
    }

}

方法2:(省空间浪费时间,推荐1,并无本质区别)

跟1相似,

  1. 第一次push时把这个数同时push进Data栈和Min栈;

  2. 把第二次push的数放入Data栈,和Min栈的栈顶元素进行比较,

  3. 如果这个数比Min栈栈顶元素大,~~则把Min栈栈顶元素push到Min栈中(这时,Min栈有两个相同的数);~~在方法2,栈顶元素更小,则不进行操作了

  4. 如果这个数比Min栈栈顶元素小于等于,则把这个数push到Min栈中,这个数就成为了新的栈顶;

  5. 总而言之,每次push时,就是把这个数push到Data数组中,同时和Min栈的栈顶元素进行比较,如果这个数比Min栈栈顶元素小于等于,则把这个数push进去,如果栈顶元素更小,把栈顶元素再次push进去,不用进行操作了;

  6. 当push完成后,Min栈栈顶元素就是最小的数了。

  7. 当发生pop操作,把Data栈和Min栈的栈顶元素同时弹出。

  8. 方法2新增pop时,方法1时Min栈和Data栈同时弹出,而2中,还要再进行比较,如果Data栈和Min栈的栈顶元素不相同,则只弹出Data栈的数;如果Data栈和Min栈的栈顶元素相同,则同时弹出Data栈和Min栈中的数;

  9. 如何实现getMin呢?就是弹出Min栈的栈顶元素。

4.如何用栈结构实现队列结构

搞两个栈。
如给1,2,3,4,5,放进push栈后,依次弹出到pop栈

在这里插入图片描述
然后再将pop栈弹出,就实现了队列结构。

注意:用时保证pop栈是空的,push弹出时要全部弹出放入到pop栈中

/**
 * @Author Z
 * @Date 2021/3/24 17:36
 * 如何用栈结构实现队列结构
 */
public class 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());
    }

}

5.如何用队列结构实现栈结构

弱智问题,但是思想上比较难想到

  1. 搞两个队列,如原Data队列和辅助Help队列。

  2. 如1,2,3,4,5;依次进入Data队列中
    在这里插入图片描述

  3. 如果要弹出的话,要表现的像一个栈,即弹出5,4,3,2,1;

  4. Data队列现在是5,4,3,2,1;现在把其中1,2,3,4弹出到Help队列中在这里插入图片描述
    把Data中的5弹出,此时Data队列里面数已经没了

  5. 把Help队列当成Data队列,把Help中3,2,1弹出到Data队列中
    在这里插入图片描述
    然后把4弹出,此时Help队列里面数已经没了

  6. 依次这样来回,就可以实现这个功能了

/**
 * @Author Z
 * @Date 2021/3/24 17:39
 * 如何用队列结构实现栈结构
 */
public class 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.peek();
            Queue<T> tmp = queue;
            queue = help;
            help = tmp;
            help.offer(ans);
            return ans;
        }

        public boolean isEmpty() {
            return queue.isEmpty();
        }

    }

}

6.可以发现图的宽度优先遍历都是经过队列实现的,图的深度优先遍历都是经过栈实现的

问:如何通过栈实现图的宽度优先遍历,通过队列实现图的深度优先遍历

通过第4和第5题,将栈用队列实现,将队列用栈实现就可以了


递归

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

1)将[L…R]范围分成左右两半。左:[L…Mid] 右[Mid+1…R]

2)左部分求最大值,右部分求最大值

3) [L…R]范围上的最大值,是max{左部分最大值,右部分最大值

public class GetMax {
    // 求arr中的最大值
    public static int getMax(int[] arr) {
        return process(arr, 0, arr.length - 1);
    }	

    // arr[L..R]范围上求最大值
    public static int process(int[] arr, int L, int R) {
        if (L == R) { // arr[L..R]范围上只有一个数,直接返回,base case
            return arr[L];
        }
        //  L..mid  mid+1...R
        // int mid = (L+R)/2
        int mid = L + ((R - L) >> 1); // 中点
        int leftMax = process(arr, L, mid);
        int rightMax = process(arr, mid + 1, R);
        return Math.max(leftMax, rightMax);
	}
}

哈希表HashMap,HashSet

无序的

时间复杂度O(1)

  1. 哈希表在使用层面上可以理解为一种集合结构

  2. 如果只有key,没有伴随数据value,可以使用HashSet结构

  3. 如果既有key,又有伴随数据value,可以使用HashMap结构

  4. 有无伴随数据,是HashMap和HashSet唯一的区别,实际结构是一回事

  5. 使用哈希表增(put)、删(remove)、改(put)和查(get)的操作,可以认为时间复杂度为O(1),但是常数时间比较大

  6. 放入哈希表的东西,如果是基础类型,内部按值传递,内存占用是这个东西的大小

  7. 放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是8字节

    map.put(key,value)

有序表TreeMap,TreeSet

时间复杂度O(logN)

有序的

  1. 有序表在使用层面上可以理解为一种集合结构

  2. 如果只有key,没有伴随数据value,可以使用TreeSet结构

  3. 如果既有key,又有伴随数据value,可以使用TreeMap结构

  4. 有无伴随数据,是TreeSet和TreeMap唯一的区别,底层的实际结构是一回事

  5. 有序表把key按照顺序组织起来,而哈希表完全不组织

  6. 红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层 具体实现不同

  7. 放入如果是基础类型,内部按值传递,内存占用就是这个东西的大小

  8. 放入如果不是基础类型,内部按引用传递,内存占用是8字节

  9. 不管是什么底层具体实现,只要是有序表,都有以下固定的基本功能和固定 的时间复杂度

  1. void put(K key, V value) 将一个(key,value)记录加入到表中,或者将key的记录 更新成value。

  2. V get(K key) 根据给定的key,查询value并返回。

  3. void remove(K key) 移除key的记录。

  4. boolean containsKey(K key) 询问是否有关于key的记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值