一、链表操作细节
1. 插入和删除操作
- 插入:在链表中插入节点时,需先检查插入位置 index 的合法性。若 index 为 0 ,执行头插操作;若 index 等于链表长度 size ,相当于尾插 。代码实现时,先创建新节点 newNode ,找到插入位置的前一个节点 prev ,然后调整指针,如 newNode.next = prev.next , prev.next = newNode 。
- 删除:删除节点时,同样要检查删除位置 index 的合法性。若 index 为 0 ,执行头删操作;若 index 等于链表长度 size - 1 ,执行尾删操作 。删除节点 toRemove 的关键是调整其前驱节点 prev 和后继节点 next 的指针,即 prev.next = next , next.prev = prev 。
2. 链表为空及特殊情况处理
当链表为空时,头指针 head 和尾指针 tail 都指向 null 。在进行插入和删除操作时,要考虑链表为空的特殊情况,以及头结点和尾结点的指针调整,如头插时 newNode.next = head , head.prev = newNode ;尾插时 tail.next = newNode , newNode.prev = tail , tail = newNode 。
二、栈的原理与Java实现
1. 栈的基本概念
栈是一种遵循后进先出(Last In First Out,LIFO)原则的数据结构 。就像一摞书,最后放上去的书会最先被拿走。栈限制了数据的操作,只支持入栈(push)、出栈(pop)和取栈顶元素等操作 。
2. Java中的栈
Java标准库中提供了 Stack 类,但它继承自 Vector ,继承了 Vector 中很多不必要的操作,设计不够纯粹。在实际使用中,空栈进行 push 操作时可能会导致程序异常退出 。也可以自定义栈类,如 MyStack ,实现基本的入栈、出栈等操作,示例代码如下:
java
class MyStack {
private java.util.Stack<String> stack = new java.util.Stack<>();
public void push(String item) {
stack.push(item);
}
public String pop() {
if (stack.isEmpty()) {
throw new RuntimeException("Stack is empty");
}
return stack.pop();
}
}
三、链表相关算法总结
1. 判断链表是否有环
- 简单方法:遍历链表,用 List 存储已访问节点,若当前节点已在 List 中,则链表有环,时间复杂度为 O(N²),空间复杂度为 O(N) 。
- 双指针法:定义快慢指针,快指针每次走两步,慢指针每次走一步。若链表无环,快指针会先到达链表末尾;若有环,快指针会追上慢指针,二者相遇,时间复杂度为 O(N),空间复杂度为 O(1) 。
2. 环形链表Ⅱ(找环入口)
使用快慢指针找到相遇点后,再设两个指针,一个从相遇点出发,一个从链表头部出发,二者每次走一步,再次相遇的位置就是环的入口 。
3. 判断链表是否为回文结构
- 空间复杂度 O(N) 的方法:复制链表并反转,然后比较原链表和反转链表是否相同 。
- 空间复杂度 O(1) 的方法:先找到链表中间节点,将后半部分链表反转,再比较前半部分和反转后的后半部分是否相同 。
4. 判断两个链表是否相交
先求出两个链表的长度,让较长链表的指针先走,走的步数为两链表长度差值,然后两个指针同时走,若相遇则链表相交 。
五、双向链表
1. 双向链表结构
双向链表的节点既有指向下一个节点的 next 指针,也有指向前一个节点的 prev 指针 ,相比单链表,双向链表在进行插入、删除等操作时更方便,无需像单链表那样先找到前驱节点,但双向链表占用更多内存空间,每个节点需额外存储一个前驱指针 。
2. 双向链表操作
在双向链表中插入节点时,需要同时调整 prev 和 next 指针;删除节点时,同样要妥善处理前后节点的指针关系,以保持链表的连贯性 。
六、编译器优化与开发效率
编译器优化是在保证代码逻辑不变的前提下,调整代码使其运行效率更高,但这也可能带来一些代价。在软件开发中,开发效率(如开发时间、人力成本等)比运行效率更受关注,特别是在当前硬件成本相对较低的情况下 。同时,提到了AI在代码生成方面的影响,指出AI可能会缩短程序员之间开发效率的差距,但也需要人来控制和监督 。