05.递归

递归

本质上,将原来的问题,转换为更小的同一问题

递归非常适合解决树类型的问题

递归:
求解最基本的问题
把原问题转换成更小的问题
注意递归函数的“宏观语义”
递归函数就是一个函数,完成一个功能。

利用递归求数组的和
datastructure.linkedList.Sum

public class datastructure.linkedList.Sum {
    public static int sum(int[] arr){
        return sum(arr, 0);
    }

    private static int sum(int[] arr, int l){
        if(l == arr.length){
            return 0;
        }

        return arr[l] + sum(arr, l + 1);
    }

    public static void main(String[] args){
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8};
        System.out.println(sum(nums));
    }
}

链表的天然递归特性

datastructure.linkedList.ListNode

public class datastructure.linkedList.ListNode {
    // leetcode中提供的节点,不能改变
    int val;
    datastructure.linkedList.ListNode next;
    datastructure.linkedList.ListNode(int x) { val = x;}

    public datastructure.linkedList.ListNode(int[] arr){
        if(arr.length == 0){
            throw new IllegalArgumentException("arr can not be empty!");
        }
        // 执行了构造函数后,this就是头节点!
        this.val = arr[0];
        datastructure.linkedList.ListNode cur = this;
        for(int i = 1; i < arr.length; i ++){
            cur.next = new datastructure.linkedList.ListNode(arr[i]);
            cur = cur.next;
        }
    }

    @Override
    public String toString(){
        StringBuilder str = new StringBuilder();
        str.append("head: ");
        datastructure.linkedList.ListNode pre = this;
        while(pre != null){
            str.append(pre.val + "->");
            pre = pre.next;
        }
        str.append("null");
        return str.toString();
    }
}

datastructure.linkedList.Solution3

public class datastructure.linkedList.Solution3 {
    // 利用递归的思路解决链表中删除值的节点。
    // 递归将问题分解成很小很小的一个可以重复执行的步骤。抽离出来
    public static datastructure.linkedList.ListNode removeElements(datastructure.linkedList.ListNode head, int val){
        // 返回当前的头结点

        // 最小规模的情况求解
        if(head == null){
            return null;
        }
        // 宏观语义上的解决了后续的删除了所有指定了的值
        head.next = removeElements(head.next, val);

        // 处理当前为head下所有的情况。
        return head.val == val? head.next:head;
    }
    public static void main(String[] args){
        int[] nums = {1, 2, 4, 6, 4, 3, 2, 6, 8, 9};
        datastructure.linkedList.ListNode head = new datastructure.linkedList.ListNode(nums);
        System.out.println(head);
        datastructure.linkedList.ListNode res = datastructure.linkedList.Solution3.removeElements(head, 2);
        System.out.println(res);
    }
}

链表的其他变形

  1. Linked List Problems 18个问题的描述

  2. 双向链表+虚拟头节点

class Node {
    E e;
    Node next, prev;
}

  1. 作业!利用递归实现增删改查:
    循环双向链表(双向+虚拟头+尾部指向虚拟头节点====java链表的实现)
public class datastructure.linkedList.CircleNodeList<E> {
    // 递归实现带有虚拟头节点的双向循环链表
    // 内部Node节点
    private class Node{
        public E e;
        Node prev, next;

        // 创建一个新的节点
        public Node(E e, Node prev, Node next){
            this.e = e;
            this.prev = prev;
            this.next = next;
        }

        // 专门创造一个虚拟头节点
        public Node(){
            this(null, null, null);
        }
    }

    private Node dommyHead;
    private int size;

    // 虚拟头节点所在的位置就是0,第一个元素所在的位置就是1,以此类推
    // 创建构造函数的时候将引用指向自己
    public datastructure.linkedList.CircleNodeList(){
        this.dommyHead = new Node();
        this.dommyHead.prev = this.dommyHead;
        this.dommyHead.next = this.dommyHead;
        size = 0;
    }

    // 添加节点
    public void add(E e, int index){
        if(index <= 0 || index > size + 1)
            throw new IllegalArgumentException("Add failed, index is illegality");

        if(index <= size / 2){
            // 正向递归
            addRecursionForwardDirection(e, index, dommyHead);
        } else {
            // 反向递归
            addRecursionReverseDirection(e, size - index + 1, dommyHead);
        }
    }

    // 添加:正向递归遍历
    private void addRecursionForwardDirection(E e, int n, Node pre){
        if(n == 1){

            Node cur = new Node(e, pre, pre.next);
            pre.next = cur;
            cur.next.prev = cur;
            size++;
        } else {
            addRecursionForwardDirection(e,n - 1, pre.next);
        }
    }

    // 添加:反向递归遍历,注意,size是将节点添加完之后才+1的
    private void addRecursionReverseDirection(E e, int n, Node next){
        if(n == 0){

            Node cur = new Node(e, next.prev, next);
            next.prev.next = cur;
            next.prev = cur;
            size++;
        } else {
            addRecursionReverseDirection(e,n - 1, next.prev);
        }
    }

    // 添加头
    public void addFirst(E e){
        // 调用正向递归
        add(e, 1);
    }

    // 添加到尾部
    public void addLast(E e){
        // 调用反向递归
        add(e, size + 1);
    }

    // 删除特定节点
    public E remove(int index){
        if(index <= 0 || index > size)
            throw new IllegalArgumentException("Remove failed, Illegality index");

        if(index <= size / 2){
            // 正向遍历
            return removeRecursionForwardDirection(index ,dommyHead);
        } else {
            // 反向遍历,删除节点的反向逻辑与添加的反向逻辑有点不同。
            // 添加是插队!,删除是出队(size - index + 1 与不加1的区别)!
            return removeRecursionReverseDirection(size - index, dommyHead);
        }
    }

    // 正向删除
    private E removeRecursionForwardDirection(int n, Node pre){
        if(n == 1){
            Node cur = pre.next;
            pre.next = cur.next;
            cur.next.prev = pre;
            size--;
            return cur.e;
        } else {
            return removeRecursionForwardDirection(n - 1, pre.next);
        }
    }

    // 反向删除
    private E removeRecursionReverseDirection(int n, Node next){
        if(n == 0){
            Node cur = next.prev;
            next.prev = cur.prev;
            cur.prev.next = next;
            size--;
            return cur.e;
        }
        else {
            return removeRecursionReverseDirection(n - 1, next.prev);
        }
    }

    // 删除第一个
    public E removeFirst(){
        return remove(1);
    }

    // 删除最后一个
    public E removeLast(){
        return remove(size);
    }

    // 删除特定值的所有节点,n 代表递归深度
    public void removeAll(E e){
        removeSpecial(dommyHead.next, e);
    }

    // 递归删除特定值的所有节点
    private void removeSpecial(Node cur, E e){
        if(cur != dommyHead){
            if(cur.e == e){
                cur.prev.next = cur.next;
                cur.next.prev = cur.prev;
                size--;
            }
            removeSpecial(cur.next, e);
        }
    }

    @Override
    public String toString(){
        if(size == 0)
            throw new IllegalArgumentException("error , empty");

        StringBuilder str = new StringBuilder();
        str.append("dommyHead->");
        Node pre = dommyHead.next;
        for(int i = 0; i < size; i++){
            str.append("<-" + pre.e + "->");
            pre = pre.next;
        }
        str.append("<-dommyHead");
        return str.toString();
    }

    public static void main(String[] args){
        datastructure.linkedList.CircleNodeList<Integer> circ = new datastructure.linkedList.CircleNodeList<>();
        circ.addLast(1);
        circ.addFirst(2);
        circ.addLast(3);
        circ.addFirst(4);
        circ.addLast(4);
        circ.addFirst(4);
        circ.addLast(3);
        circ.addFirst(3);
        circ.addLast(4);
        circ.addFirst(4);
        System.out.println(circ);
        circ.removeAll(4);
        System.out.println(circ);
    }
}

提供了增加,删除的功能

  1. 数组链表(记录索引)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • 小练习1:N的阶乘 递归需要注意的两点:1.找规律、2.找出口。没有出口的话递归就会无限死循环,所以必须用一个已知...
    耦耦阅读 185评论 0 0
  • 这里拥有辽阔的土地,蔚蓝的天空,无边无际的大海,在大海边有一处小山村,这里大多打渔为生,其中一户靠近偏僻角落的...
    沐阳笑阅读 624评论 0 0
  • 那一年,不谙世事,从长沙经成都一路火车到了云南。高校有学不好好上,只身一人到处跑,难免被怀疑精(nao)力...
    小思Yookee阅读 452评论 0 4
  • 《想想1409》epub下载地址《想想1409》豆瓣阅读地址《想想1409》多看阅读地址《想想1409》拇指阅读地...
    简书阅读 874评论 3 8