数据结构基础复习-数组、链表、二叉树

1. 数据存储方式

在这里插入图片描述

  • 数组(顺序存储):需要考虑扩容问题
  • 链表(链式存储):需要分配空间存储节点指针

2. 数据结构的实现

在这里插入图片描述

3. 数据结构的操作

基本操作:遍历 -> 访问 -> CRUD

数组遍历访问

void traverse(int[] arr) {
	for (int i = 0; i < arr.length; i++) {
		//迭代访问 arr[i]
	}
}

单链表遍历访问

class ListNode {
	int val;
	ListNode next;
}

//使用循环
void traverse(ListNode head) {
	for (ListNode p = head; p != null; p = p.next) {
		//迭代访问 p.val
	}
}

//使用递归
void traverse(ListNode head) {
	//递归访问 head.val
	traverse(head.next);
}

二叉树遍历访问

class ListNode {
	int val;
	ListNode left;
	ListNode right;
}

//前序遍历,结果形式总是 [ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
void traverse(ListNode head) {
	//递归访问 head.val
	traverse(head.left);
	traverse(head.right);
}

//中序遍历,结果形式总是 [ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]
void traverse(ListNode head) {
	traverse(head.left);
	//递归访问 head.val
	traverse(head.right);
}

//后序遍历,结果形式总是[ [左子树的中序遍历结果], [右子树的中序遍历结果], 根节点 ]
void traverse(ListNode head) {
	traverse(head.left);
	traverse(head.right);
	//递归访问 head.val
}

递归

  1. 明确函数的功能,重点关注如何处理子问题的结果来获得整个问题的解
  2. 寻找递归结束条件,即参数满足什么条件,递归应该结束并返回结果
  3. 缩小参数范围,即寻找参数间的关系,使问题保持一致的情况下缩小规模

4. 数组(查、改快)

4.1 二分查找

  • target 唯一时,返回其索引
public int binarySearch(int[] nums, int target) {
	int l = 0;
	int h = nums.length - 1;	//区间右闭

	while (l <= h) {	//因为区间右闭,所以要加等号,终止条件 l == h + 1
		int m = l + (h - l) / 2;	//防止大数溢出
		if (nums[m] == target) {
			return m;
		} else if (nums[m] < target) {
			l = m + 1;
		} else if (nums[m] > target) {
			h = m - 1;
		}
	}
	return -1;	
}


lower_bound 最下边界问题(leetcode 875、1011)

  • target 不唯一时或不存在时,返回第一个大于或等于 target 元素的索引
public int lower_bound(int[] nums, int target) {
	int l = 0;
	int h = nums.length;	//区间右开

	while (l < h) {		//因为区间右开,不需要等号,终止条件 l == h
		int m = l + (h - l) / 2;	//[l, m) m [m+1, h)
		if (nums[m] == target) {
			h = m;
		} else if (nums[m] < target) {
			l = m + 1;
		} else {
			h = m;
		}
	}
	return l;
}

upper_bound 最上边界问题

  • target 不唯一时或不存在时,返回第一个大于 target 元素的索引
public int upper_bound(int[] nums, int target) {
	int l = 0;
	int h = nums.length;	//区间右开

	while (l < h) {		//因为区间右开,不需要等号,终止条件 l == h
		int m = l + (h - l) / 2;	//[l, m) m [m+1, h)
		if (nums[m] == target) {
			l = m + 1;
		} else if (nums[m] < target) {
			l = m + 1;
		} else {
			h = m;
		}
	}
	return l;
}

5. 链表(增、删快)

单链表 操作
在这里插入图片描述

cur.next = prev.next;
prev.next = cur;

单链表 操作
在这里插入图片描述

prev.next = prev.next.next;

善用 快慢指针哈希表 可以解决一些链表问题(leetcode 142、19)

快慢指针模板如下:

ListNode slow = head;
ListNode fast = head;
/* 获取空节点的下一个节点将导致空指针错误
因此在运行 fast = fast.next.next 之前,需要检查 fast 和 fast.next 不为空
注意fast的步长,以及满足何种条件最终和slow可以同步,防止终止条件不正确导致无限循环 */
while (slow != null && fast != null && fast.next != null) {
    slow = slow.next;           // move slow pointer one step each time
    fast = fast.next.next;      // move fast pointer two steps each time
    if (slow == fast) {         // change this condition to fit specific problem
        return true;
    }
}
return false;   // change return value to fit specific problem

使用 递归 反转链表

ListNode reverse(ListNode head) {
	if (head == null) return null;
    if (head.next == null) return head;
    ListNode last = reverse(head.next);	//反转子链表
    head.next.next = head;	//子链表最后的节点由指向null改为指向head
    head.next = null;	//head称为最后节点,重新指向null
    return last;	//返回反转后的链表
}

6. 二叉树

6.1 广度优先搜索(BFS)

  • 层序遍历使用队列作为辅助结构
  • 核心为当前层的所有节点依次出队,同时下一层的所有节点依次入队

leetcode 102 二叉树的层序遍历
在这里插入图片描述
迭代法:

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        
        List<List<Integer>> list = new ArrayList<>();
        if (root == null) return list;
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
        	List<Integer> level = new ArrayList<>(); //当前层的节点值列表
        	int curlev = queue.size();	//当前层节点个数
        	for (int i = 1; i <= curlev; i++) {	//将当前层每个节点出队,其子节点入队
        		TreeNode node = queue.poll();
        		level.add(node.val);
        		if (node.left != null) {
        			queue.offer(node.left);
        		}
        		if (node.right != null) {
        			queue.offer(node.right);
        		}
        	}
        	list.add(level);
        }
        return list;
    }
}

递归法

class Solution {

	List<List<Integer>> list = new ArrayList<>();

    public List<List<Integer>> levelOrder(TreeNode root) {
    	if (root == null) return list;
    	addVal(root, 0); //给指定层依次添加对应节点的节点值
    	return list;
    }

    public void addVal(TreeNode root, int levelNum) {
    	
    	if (list.size() == levelNum) {
    		List<Integer> level = new ArrayList<>();
    		list.add(level);
    	}
    	list.get(levelNum).add(root.val);

    	if (root.left != null) {
    		addVal(root.left, levelNum + 1);
    	}

    	if (root.right != null) {
    		addVal(root.right, levelNum + 1);
    	}
    }
}

6.2 序列化

String SEP = ",";
String NULL = "#";

String serialize(TreeNode root) {
	StringBuilder sb = new StringBuilder();
	serialize(root, sb);
	return sb.toString();	
}

void serialize(TreeNode root, StringBuilder sb) {
	if (root == null) {
		sb.append(NULL).append(SEP);
	}
	//前序
	sb.append(root.val).append(SEP);
	serialize(root.left, sb);
	serialize(root.right, sb);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值