数据存储结构本质上只有两种:数组和链表,高级的数据结构都是从以上两种基础上衍生出来的;对于任何数据结构,其基本功能无非是适用于不同场景的增删改查。遍历的方式本质上也有两种:迭代和递归,迭代是线性的,人的大脑的思维习惯更容易理解;递归相对的抽象,但是了解递归的本质后,我们会发现递归在解决非线性数据结构的遍历有着非常好的效果。下面我们来看集中典型的数据结构是如何遍历的:
//数组的遍历,典型的线性遍历
void traverse(int[] array) {
for (int i = 0; i < array.length; i++) {
//迭代访问
}
}
//链表的遍历,线性和递归均可
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
this.next = null;
}
}
//线性遍历
void traverse(ListNode head){
for(ListNode p = head; p != null; p = p.next){
//访问p.val
}
}
//递归遍历
void traverse(ListNode head){
//广义上这里是一个递归的出口,对于链表,递归出口唯一
if(head == null){
return;
}
//前序遍历head.val
traverse(p.next);
//后序遍历head.val
}
//二树的遍历,线性的需要手动借助栈实现,下面以递归方式展示
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
void traverse(TreeNode root) {
//递归出口,本质上叶子节点都是出口
if(root == null){
return;
}
//前序遍历root.val
traverse(root.left);
//中序遍历root.val
traverse(root.right);
//后序遍历root.val
}
//N树的遍历,线性的需要手动借助栈实现,下面以递归方式展示
class TreeNode {
int val;
TreeNode[] child;
TreeNode(int val,int n) {
this.val = val;
child = new TreeNode[n];
}
}
void traverse(TreeNode root) {
//递归出口,本质上叶子节点都是出口
if(root == null){
return;
}
for(TreeNode child : root.child){
traverse(child);
}
}