ref:
链表的概念
什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表的入口节点称为链表的头结点也就是head。
java链表有三种类型,分别是单链表、双链表、和循环链表
以下用java定义了一个双链表节点类
//定义一个Node 类,Node 对象 表示双向链表的一个结点
class Node {
public Object item; //真正存放数据
public Node next; //指向后一个结点
public Node pre; //指向前一个结点
public Node(Object name) {
this.item = name;
}
public String toString() {
return "Node name=" + item;
}
}
注意,需要定义一个构造函数,方便直接给数据域赋值
链表的存储方式
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。
链表是通过指针域的指针链接在内存中各个节点。
所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
链表和数组的性能对比
203移除元素
题目:给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点
自解:
/**
* 203移除链表元素(自解)
* @param head
* @param val
* @return
*/
public ListNode removeElements(ListNode head, int val) {
ListNode index=head;
ListNode result=head;
ListNode pre=null;
while(index!=null){
if(index.val==val){
if(pre==null){
result=index.next;
}else{
pre.next=index.next;
}
}else{
pre=index;
}
index=index.next;
}
return result;
}
代码随想录提供了一种使用虚结点的方式
* 添加虚节点方式(随想录)
* 时间复杂度 O(n)
* 空间复杂度 O(1)
* @param head
* @param val
* @return
*/
public ListNode removeElements01(ListNode head, int val) {
if (head == null) {
return head;
}
// 因为删除可能涉及到头节点,所以设置dummy节点,统一操作
ListNode dummy = new ListNode(-1, head);
ListNode pre = dummy;
ListNode cur = head;
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
return dummy.next;
}
这种方案异常的简洁,就是在原有链表基础上在head节点之前加了一个节点。
这里提供一个主方法方便测试:
public class day03 {
public static void main(String[] args) {
//定义一个测试的数据3,2,3,5,3
ListNode node0=new ListNode(3);
ListNode node1=new ListNode(1);
node0.next=node1;
ListNode node2=new ListNode(3);
ListNode node3=new ListNode(3);
node1.next=node2;
node2.next=node3;
ListNode node4=new ListNode(1);
node3.next=node4;
node4.next=null;
//203移除链表元素
ListNode newlianbiao=new day03().removeElements(node0,3);
new day03().bianli(newlianbiao);
}
public void bianli(ListNode head){
ListNode headnew=head;
while(headnew!=null){
System.out.print(headnew.val+",");
headnew=headnew.next;
}
}
}
707设计一个链表类
以单链表为例,设计一个运用了虚节点思想的可操作链表类
//707设计链表
class MyLinkedList {
int size;
//虚拟头结点
ListNode head;
public MyLinkedList() {
this.size=0;
this.head=new ListNode(-1);
}
/**
* 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
* @param index
* @return
*/
public int get(int index) {
if(index<0||index>size-1){
return -1;
}
ListNode result=this.head;
for(int i=0;i<index+1;i++){
result=result.next;
}
return result.val;
}
public void addAtHead(int val) {
addAtIndex(0,val);
}
public void addAtTail(int val) {
addAtIndex(size,val);
}
/**
*将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index
* 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
* @param index
* @param val
*/
public void addAtIndex(int index, int val) {
//判断插入条件
if(index<0){
return;
}else if(index>size){
return;
}
size++;
ListNode newnode=new ListNode(val);
ListNode pre=this.head;
ListNode pointer=pre.next;
for(int i=0;i<index;i++){
pre=pointer;
pointer=pointer.next;
}
pre.next=newnode;
newnode.next=pointer;
}
public void deleteAtIndex(int index) {
//判断插入条件
if(index<0){
return;
}else if(index>size-1){
return;
}
size--;
ListNode pre=this.head;
ListNode pointer=pre.next;
for(int i=0;i<index;i++){
pre=pointer;
pointer=pointer.next;
}
pre.next=pointer.next;
}
}
206反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
这道题解法是复制每一个链表上的节点,重新造一个反过来的链表
/**206. 反转链表
*
*/
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode now=head;
ListNode newpre=null,newnow=null;
while(now!=null){
newnow=new ListNode(now.val);
newnow.next=newpre;
newpre=newnow;
now=now.next;
}
return newnow;
}