程序员老鬼

被裁公司给了20 万的补偿,结果前领导找到我,说让跟他一起创业,每周干3天,保底挣3w~

最近一位同事找我吐槽,他一直在纠结是不是该跳槽去创业。

Image

他前公司给了20万的补偿,但他本来是想找到一个新的工作,结果就遇到这个“创业诱惑”。领导承诺每周三天,保底3W,听起来简直是捡钱

但是,我得说,创业不是咱们做一个功能上线的事,更多时候是“填坑”。就像我做项目时,总是想着只要代码写好就没问题,结果总会遇到各种bug,光想法好是不够的,执行上的每一步都需要小心翼翼。

我觉得,创业没那么简单,起码要保证有一定的积蓄,并且能承担起失败的风险。

如果没有创业经验,最好先在公司里稳扎稳打,获取一些能提升自己的资源,再做决定。

毕竟,想创业的不是一时头脑发热,真要走那条路,心理准备要充足。【备注:文末可领最新资料】。

算法题:设计链表

今天我们聊聊一个经典的算法题:设计链表。

说到链表,首先得说明一个概念:链表是一种线性的数据结构,它不像数组那样存储在连续的内存空间中,而是每个元素(节点)都包含数据和指向下一个元素的指针(或引用)。这也就是链表和数组最大的区别,链表的内存不是连续的,它的每个元素存储在内存中不同的地方,但是每个节点会“知道”下一个节点在哪里。

链表的一些常见操作包括:添加元素、删除元素、查找元素、反转链表等等。这些操作常常是面试中考察的重点,尤其是需要实现一个自定义的链表类。

让我们先从简单的“设计一个链表”开始,定义一个链表节点类。假设我们只做单链表,不考虑双向链表或者循环链表的复杂情况。简单来说,链表节点的结构大概就应该是这样:

class ListNode {
    int val;
    ListNode next;

        // 构造函数
    ListNode(int x) {
        val = x;
        next = null;
    }
}

上面是链表节点类的简单定义。val存储节点的值,next是指向下一个节点的引用。

接下来,我们需要一个链表类来管理这些节点。链表类需要支持基本的操作,例如添加节点、删除节点、打印链表等。

假设我们要实现一个支持以下操作的链表类:

  1. addAtHead(int val):在链表头部添加一个节点。
  2. addAtTail(int val):在链表尾部添加一个节点。
  3. deleteAtIndex(int index):删除指定位置的节点。
  4. printList():打印链表的所有节点。

具体代码如下:

class MyLinkedList {
    private ListNode head; // 链表的头结点
    private int size; // 链表的长度

    // 初始化链表
    public MyLinkedList() {
        head = new ListNode(0); // 虚拟头节点,方便操作
        size = 0;
    }

    // 在头部添加节点
    public void addAtHead(int val) {
        ListNode node = new ListNode(val);
        node.next = head.next;
        head.next = node;
        size++;
    }

    // 在尾部添加节点
    public void addAtTail(int val) {
        ListNode node = new ListNode(val);
        ListNode cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = node;
        size++;
    }

    // 删除指定位置的节点
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) {
            return; // 如果索引无效,直接返回
        }

        ListNode cur = head;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        cur.next = cur.next.next;
        size--;
    }

    // 打印链表
    public void printList() {
        ListNode cur = head.next;
        while (cur != null) {
            System.out.print(cur.val + " -> ");
            cur = cur.next;
        }
        System.out.println("null");
    }
}

现在,我们来分析下每个操作:

  1. addAtHead(int val):我们创建一个新的节点,把它的next指向当前头节点的下一个节点。然后更新头节点的next指向这个新节点。时间复杂度是O(1)。

  2. addAtTail(int val):我们从头节点开始遍历,直到找到链表的最后一个节点,将其next指向新节点。时间复杂度是O(n),因为我们可能需要遍历整个链表。

  3. deleteAtIndex(int index):首先检查索引是否合法,然后通过遍历找到前一个节点,修改它的next指向要删除节点的下一个节点。时间复杂度是O(n),因为最坏情况下我们需要遍历整个链表。

  4. printList():遍历链表,输出每个节点的值。时间复杂度是O(n)。

说到链表,除了基本的操作,还有一些更有挑战性的操作,比如链表反转。反转链表的算法是经典的面试题之一。反转链表的思路是:我们遍历链表,同时改变每个节点的next指向,使得链表的方向发生反转。下面是反转链表的实现代码:

public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;

        while (curr != null) {
        ListNode nextTemp = curr.next; // 暂存下一个节点
        curr.next = prev; // 反转当前节点的指向
        prev = curr; // 移动prev和curr指针
        curr = nextTemp;
    }
    return prev; // 返回新的头节点
}

这个算法的时间复杂度是O(n),空间复杂度是O(1),因为我们只用了常数级别的额外空间。

讲了这么多链表的实现,大家可能会觉得链表其实挺简单的,操作也不复杂。但其实,链表的细节操作还是有点讲究的,尤其是涉及到边界条件时。比如,删除头节点、空链表的处理、以及反转链表时指针的操作,每一步都得小心翼翼。

-END-

ok,今天先说到这,老规矩,给大家分享一份不错的副业资料,感兴趣的同学找我领取。

Image

以上,就是今天的分享了,看完文章记得右下角给何老师点赞,也欢迎在评论区写下你的留言。