链表理论基础
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
- 单链表
- 双链表
- 循环链表(可以用来解决约瑟夫环问题。)
总结:
链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
移除链表元素
力扣连接:203.移除链表元素(简单)
使用java ,python的话就不用手动管理内存
这里就涉及如下链表操作的两种方式:
- 直接使用原来的链表来进行删除操作。
- 设置一个虚拟头结点在进行删除操作。
1.代码
//不用额外的结点
public ListNode removeElements(ListNode head, int val) {
//删除头结点
while(head!=null&&head.val==val){
head = head.next;
}
ListNode res = head;
//删除非头结点
while(res!=null){
while(res.next!=null&&res.next.val==val){
res.next = res.next.next;
}
res = res.next;
}
return head;
}
设计链表
力扣连接:707.设计链表(中等)
设置一个虚拟头结点(这样更方便一些)
代码
class ListNode {
Integer val;
ListNode next;
public ListNode(){}
public ListNode(Integer val){this.val = val;}
public ListNode(Integer val, ListNode next){
this.val = val;
this.next = next;
}
}
class MyLinkedList {
ListNode node;
Integer len;
public MyLinkedList() {
node = new ListNode(-1);
len = 0;
}
public int get(int index) {
//越界情况
if(index>=len || index<0) return -1;
ListNode p = findPrev(index);
return p.next.val;
}
public void addAtHead(int val) {
addAtIndex(0,val);
}
public void addAtTail(int val) {
addAtIndex(len,val);
}
public void addAtIndex(int index, int val) {
//越界情况
if(index>len) return;
if(index<0) index = 0;
//找前驱
ListNode p = findPrev(index);
ListNode gen = new ListNode(val);
//插入前驱后
gen.next = p.next;
p.next = gen;
len++;
}
public void deleteAtIndex(int index) {
//越界情况
if(index>=len || index<0) return;
ListNode p = findPrev(index);
p.next = p.next.next;
len--;
}
//获取前驱结点
public ListNode findPrev(int index){
ListNode prev = node;
for(int i = 0;i<index;i++){
prev = prev.next;
}
return prev;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
反转链表
力扣连接:206. 反转链表(简单)
1.代码(双指针法)
public ListNode reverseList(ListNode head) {
if(head==null) return null;
ListNode prev = null;
ListNode front = head;
while(front!=null){
//保存front的下一个结点
ListNode temp = front.next;
//front的下一个结点指向prev
front.next = prev;
//prev指针向前
prev = front;
//front指针向前
front = temp;
}
return prev;
}
注意:
- 首先要把 cur->next 节点用tmp指针保存一下。
2.代码(递归法)
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
private ListNode reverse(ListNode prev, ListNode cur) {
if (cur == null) {
return prev;
}
ListNode temp = null;
temp = cur.next;// 先保存下一个节点
cur.next = prev;// 反转
// 更新prev、cur位置
// prev = cur;
// cur = temp;
return reverse(cur, temp);
}
总结
这一次学习了链表的相关知识,了解了链表的增删查
一般建立一个新的结点指向头结点会比在原链表上操作容易一些。
判断是否为null的对象一般是跟随执行内容改变
if(prev!=null){
prev=prev.next;
}
if(prev.next!=null){
prev.next=prev.next.next;
}