数据结构及算法学习Part 1

代码gitee仓库地址

  1. 什么是链表、队列、栈?
    他们都是一种逻辑上的数据结构,都可以使用数组来实现
  • 链表:是一种拥有后或前后节点指针的数据结构,前者为单向链表,后者为双向链表,链表查找慢,增删快
  • 队列:特点是先进先出,查找和增删都慢
  • 栈:特点是先进后出,查找和增删都慢
  1. 栈和队列常见问题
  • 如何使用固定数组实现队列、栈?
    • 队列:使用三个变量分别维护,size队列中元素的数量,begin出队元素索引,end添加到队列元素索引,是否可以向队列中添加元素,判断size的大小即可,如果size小于队列长度即可添加,反之不可;队列添加元素,end++,如果end大于了队列长度-1,则end=0,arr[end] = 元素,size++;队列出队元素,arr[begin]即出队元素,begin++,如果end大于了队列长度-1,则end=0,size–;
      代码实现如下
public class ArrayToQueue<T> {

	/**
	 * 队列中元素数量
 	 */
	private int size;

	/**
	 * 出队元素索引
	 */
	private int begin;

	/**
	 * 添加到队列元素索引
	 */
	private int end;

	/**
	 * 数组
	 */
	private T[] arr;

	public ArrayToQueue(int length) {
		arr = (T[]) new Object[length];
	}

	/**
	 * 是否为空
	 * @return true or false
	 */
	public boolean isEmpty() {
		return size == 0;
	}

	/**
	 * 获取队列元素数量
	 * @return 队列元素数量
	 */
	public int getSize() {
		return this.size;
	}
	
	/**
	 * 添加元素至队列中
	 */
	public void add(T obj) {
		if (size >= arr.length) {
			throw new RuntimeException("队列已满");
		}
		// 队列元素+1
		size++;
		// 添加到数组中end位置
		arr[end] = obj;
		// 获取end所在数组的下一个索引
		end = getIndex(end);
	}

	/**
	 * 获取并移除队列第一个元素
	 * @return 队列第一个元素
	 */
	public T poll() {
		if (size <= 0) {
			throw new RuntimeException("队列无数值");
		}
		// 队列元素-1
		size--;
		T result = arr[begin];
		// 获取begin所在数组的下一个索引
		begin = getIndex(begin);
		return result;
	}

	/**
	 * 获取所在数组的下一个索引
	 * @param currentIndex 当前索引
	 * @return 下一个索引
	 */
	private int getIndex(int currentIndex) {
		return currentIndex < arr.length - 1 ? currentIndex + 1 : 0;
	}

	public static void main(String[] args) {
		ArrayToQueue<Integer> queue = new ArrayToQueue<>(5);
		queue.add(1);
		queue.add(2);
		System.out.println(queue.poll());
		queue.add(3);
		queue.add(4);
		System.out.println(queue.poll());
		queue.add(5);
		System.out.println(queue.poll());
		System.out.println(queue.poll());
		System.out.println(queue.poll());
	}
}

栈:使用一个index维护当前数据中最大位置的索引,向栈中添加元素时,给arr[index]赋值,index不能大于数组长度-1,弹出元素时,arr[index]=0,index减一,index不能小于0;

public class ArrayToStack<T> {

	/**
	 * 数组存在元素最大位置索引
	 */
	private int index;

	/**
	 * 数组
	 */
	private T[] arr;

	/**
	 * 栈是否为空
	 * @return true or false
	 */
	public boolean isEmpty() {
		return index == 0;
	}

	/**
	 * 栈是否满了
	 * @return true or false
	 */
	public boolean isFull() {
		return index == arr.length;
	}

	public ArrayToStack(int size) {
		index = 0;
		arr = (T[]) new Object[size];
	}

	/**
	 * 压栈
	 * @param obj 压栈元素
	 */
	public void push(T obj) {
		if (isFull()) {
			throw new RuntimeException("栈已满");
		}
		arr[index] = obj;
		index++;
	}

	/**
	 * 出栈
	 * @return 出栈元素
	 */
	public T pop() {
		if (isEmpty()) {
			throw new RuntimeException("栈中无元素");
		}
		index--;
		T result = arr[index];
		return result;
	}

	/**
	 * 获取栈顶元素但不移除
	 * @return 栈顶元素
	 */
	public T peek() {
		if (isEmpty()) {
			throw new RuntimeException("栈中无元素");
		}
		return arr[index - 1];
	}

	public static void main(String[] args) {
		ArrayToStack<Integer> stack = new ArrayToStack<>(5);
		stack.push(1);
		System.out.println(stack.pop());
		stack.push(2);
		System.out.println(stack.pop());
		stack.push(3);
		stack.push(4);
		stack.push(5);
		System.out.println(stack.pop());
		System.out.println(stack.pop());
		System.out.println(stack.pop());
	}
}
  • 设计一个栈,让pop、push、getMin操作的时间复杂度都是O(1)
    pop、push不在赘述,getMin即获取当前栈中最小的元素,思路就是额外使用一个栈维护当前栈中最小的元素,比如压栈1,最小栈同时压1,压栈2,最小栈还是压1,压栈3,最小栈还是压1,压栈0,最小栈压0,依此,栈 1 - 2 - 3 - 0,最小栈就是1 - 1 - 1 -0,弹栈的同时最小栈也弹栈,利用空间换时间想法。
public class MinStack {

	/**
	 * 正常操作的栈
	 */
	private ArrayToStack<Integer> opStack;

	/**
	 * 记录当前栈中最小元素的栈
	 */
	private ArrayToStack<Integer> minStack;

	public MinStack(int size) {
		opStack = new ArrayToStack<>(size);
		minStack = new ArrayToStack<>(size);
	}

	/**
	 * 弹栈
	 * @return 栈顶元素
	 */
	private Integer pop() {
		Integer result = opStack.pop();
		// 最小栈也弹栈
		minStack.pop();
		return result;
	}

	/**
	 * 入栈
	 * @param obj 入栈元素
	 */
	public void push(Integer obj) {
		// 压栈
		opStack.push(obj);
		// 正常操作栈压一个元素 最小栈也压一个元素
		if (minStack.isEmpty()) {
			// 最小栈为空直接压栈
			minStack.push(obj);
		} else {
			// 最小栈不为空 则将obj与最小栈顶元素比较 最小的入栈
			minStack.push(minStack.peek() > obj ? obj : minStack.peek());
		}
	}

	/**
	 * 获取当前栈最小元素
	 * @return 当前栈最小元素
	 */
	private Integer getMin() {
		return minStack.peek();
	}

	public static void main(String[] args) {
		MinStack minStack = new MinStack(5);
		minStack.push(3);
		System.out.println(minStack.getMin());
		minStack.push(2);
		System.out.println(minStack.getMin());
		minStack.push(4);
		System.out.println(minStack.getMin());
		minStack.push(1);
		System.out.println(minStack.getMin());
		minStack.push(5);
		System.out.println(minStack.getMin());
		System.out.println("--------");
		System.out.println(minStack.pop());
		System.out.println(minStack.pop());
		System.out.println(minStack.pop());
		System.out.println(minStack.pop());
		System.out.println(minStack.pop());
	}
}
  • 如何使用栈实现队列?
    栈先进先出、队列先进后出,使用两个栈,一个pop栈,一个push栈,默认都是空,如果入队元素则向push栈中压,压完栈,立刻弹栈则从push栈弹出,按照弹出的顺序压栈到pop栈,只有pop栈为空时,push才可以向pop压栈,获取元素则从pop栈弹栈获取,没有则从push弹栈到pop栈再弹出。
public class StackToQueue<T> {

	/**
	 * 弹栈的栈
	 */
	private ArrayToStack<T> popStack;

	/**
	 * 压栈的栈
	 */
	private ArrayToStack<T> pushStack;

	public StackToQueue(int size) {
		popStack = new ArrayToStack<T>(size);
		pushStack = new ArrayToStack<T>(size);
	}

	/**
	 * 入队
	 * @param obj 入队元素
	 */
	public void add(T obj) {
		// 压push栈
		pushStack.push(obj);
		pushToPop();
	}

	/**
	 * 出队
	 * @return 出队元素
	 */
	public T get() {
		if (pushStack.isEmpty() && popStack.isEmpty()) {
			throw new RuntimeException("队列为空");
		}
		pushToPop();
		return popStack.pop();
	}

	/**
	 * push栈导向pop栈
	 */
	public void pushToPop() {
		// pop栈为空才可以将push栈全部弹栈压入pop栈
		if (popStack.isEmpty()) {
			// 将push栈弹入pop栈
			while (!pushStack.isEmpty()) {
				popStack.push(pushStack.pop());
			}
		}
	}

	public static void main(String[] args) {
		StackToQueue<Integer> stackToQueue = new StackToQueue<>(5);
		stackToQueue.add(1);
		System.out.println(stackToQueue.get());
		stackToQueue.add(2);
		stackToQueue.add(2);
		System.out.println(stackToQueue.get());
		stackToQueue.add(3);
		System.out.println(stackToQueue.get());
		stackToQueue.add(4);
		System.out.println(stackToQueue.get());
		System.out.println(stackToQueue.get());
	}
}
  • 如何使用队列实现栈
    栈先进先出、队列先进后出,使用两个队列,入队时,默认向某一个添加元素,出队时,将默认队列中只留一个元素,剩下全部出队在入队到另一个队列,返回默认队列最后一个元素,再交换两个队列的值。
public class QueueToStack<T> {

	private ArrayToQueue<T> queue;

	private ArrayToQueue<T> help;

	public QueueToStack(int size) {
		queue = new ArrayToQueue<T>(size);
		help = new ArrayToQueue<T>(size);
	}

	/**
	 * 压栈
	 * @param obj 压栈元素
	 */
	public void push(T obj) {
		queue.add(obj);
	}

	/**
	 * 弹栈
	 * @return 栈顶元素
	 */
	public T pop() {
		if (queue.isEmpty()) {
			throw new RuntimeException("栈中无元素");
		}
		// queue只留一个元素 用来弹栈
		while (queue.getSize() > 1) {
			help.add(queue.poll());
		}
		T result = queue.poll();
		ArrayToQueue<T> temp = queue;
		queue = help;
		help = temp;
		return result;
	}

	public static void main(String[] args) {
		QueueToStack<Integer> stack = new QueueToStack<>(5);
		stack.push(1);
		stack.push(2);
		System.out.println(stack.pop());
		stack.push(3);
		stack.push(4);
		System.out.println(stack.pop());
		stack.push(5);
		System.out.println(stack.pop());
		System.out.println(stack.pop());
		System.out.println(stack.pop());
	}
}
  1. 递归
    将一个问题拆为n多个同样的子问题,子问题的解决使用的都是同一个方法
    举个例子,获取数组中最大的元素,获取[1,2,3,4]最大元素,按照递归的思路,获取0~3最大元素,拆分为,获取0-1最大元素,获取2-3最大元素,两者的最大值即为数组中最大元素,0-1最大元素,又是0-0和1-1两个最大元素中最大值,依此类推
    递归最重要在于找到base case,即(子)问题何时结束,新手一定要画递归图,分析并理解递归。
public class Recurrence {

	public static void main(String[] args) {
		int[] arr = new int[]{1,2,200,4,5,6,7};
		System.out.println(getMax(arr, 0, arr.length - 1));;
	}

	private static int getMax(int[] arr, int begin, int end) {
		if (begin == end) {
			return arr[begin];
		}
		int mid = begin + (end - begin) / 2;
		int leftMax = getMax(arr, begin, mid);
		int rightMax = getMax(arr, mid + 1, end);
		return Math.max(leftMax, rightMax);
	}
}

递归图如下,最后得到最大值为200。
在这里插入图片描述

  1. Master公式
    用来推算递归算法的时间复杂度,前提是递归算法的子问题的规模都是一致的时候才可以使用
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值