文章目录
- LinkedList笔记
-
- [一、 LinkedList](#一、 LinkedList)
- 二、MyLinkedList的实现
-
- 1.定义内部类
- 2.打印链表、求链表长度、判断是否包含关键字
- [3. 头插法和尾插法](#3. 头插法和尾插法)
- 4.在任意位置插入
- 5.删除结点
- 6.清空链表
LinkedList笔记
一、 LinkedList
1.概念
LinkedList的底层是一个双向链表
- 在插入和删除时,不用挪动元素
- 在获取尾部结点时,不需要遍历获取,直接利用last结点
2.LinkedList的构造方法
- 分为无参构造和有参构造
有参:使用其他集合容器中元素构造List
在构造LinkedList的时候,传递的参数的类型要满足指定泛型的上界,同时要实现Collection接口
3.LinkedList的遍历
分别用重写的print方法、foreach、迭代器进行遍历
java
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("hello");
linkedList.add("world");
linkedList.add("!");
linkedList.add("?");
linkedList.add("|");
System.out.println(linkedList);
System.out.println("-----------");
for (String x:linkedList) {
System.out.print(x+" ");
}
System.out.println();
System.out.println("-----------");
//使用迭代器遍历-正向遍历
ListIterator<String> it = linkedList.listIterator();
while (it.hasNext()){
System.out.print(it.next()+" ");
}
System.out.println();
System.out.println("===========");
ListIterator<String> rit = linkedList.listIterator(linkedList.size());
while (rit.hasPrevious()){
System.out.print(rit.previous()+" ");
}
//使用迭代器遍历-反向遍历
}
二、MyLinkedList的实现
1.定义内部类
与单链表不同的是,双链表的结点新增了prev域
java
public class MyLinkedList {
static class ListNode{
public int val;
public ListNode prev;//前驱
public ListNode next;//后继
public ListNode(int val) {//构造方法
this.val = val;
}
}
public ListNode head;//定义头结点
public ListNode last;//定义尾结点
1.在内部类中定义结点的元素
2.定义构造器
3.创建头/尾结点
2.打印链表、求链表长度、判断是否包含关键字
与单链表的形式相同
java
public void disPlay(){
ListNode cur = head;
while (cur!=null){
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
/**
*求链表长度
* @return int
*/
public int size(){
ListNode cur = head;
int count = 0;
while (cur!=null){
count++;
cur = cur.next;
}
return count;
}
/**
* 查看在链表中是否包含关键字key
* @param key
* @return
*/
public boolean contains(int key){
ListNode cur = head;
while (cur != null){
if (cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
3. 头插法和尾插法
java
/**
* 头插法
* o(1)
* @param data
*/
public void addFirst(int data){
ListNode node = new ListNode(data);
if (head ==null){
head = node;
last = node;
}else {
node.next = head;
head.prev = node;
head = node;//头结点前移
}
}
/**
* 尾插法o(1)
*/
public void addLast(int data){
ListNode node = new ListNode(data);
if(head==null){
head = node;
last = node;
}else{
last.next = node;
node.prev = last;
last = node;
}
}
因为尾插的时候有last结点,不用进行尾结点的遍历查找
所以双链表尾插的时间复杂度是 o(1)
4.在任意位置插入
java
public void addIndex(int index, int data) {
if (index < 0 || index > size()) {//判断索引是否超出
return;
}
if (index == 0) {//利用头插
addFirst(data);
return;
}
if (index == size()) {//利用尾插
addLast(data);
return;
}
ListNode node = new ListNode(data);
ListNode cur = head;
while (index!=0){//找到索引的位置
cur = cur.next;
index--;
}
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
1.先判断索引下标是否溢出
2.如果索引是开头或者末尾的位置,调用写好的头插法和尾插法
3.通过遍历找到索引的位置cur
4.将cur插入链表中
- 先改变node的next域,
- 将node与cur相连 将cur的前驱的next域,改为node,
- 将node与cur的前一个结点相连
- 将node前驱改为cur前驱的地址 将cur的前驱改为node的地址值
5.删除结点
java
public void remove(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val == key) {
if (cur == head) {//删的是头的情况
head = head.next;
if (head!=null){//如果只有一个结点,移动后前驱不需要置空
head.prev = null;//head的前驱置为空
}
} else {//删除中间或者尾部
cur.prev.next = cur.next;
if (cur == last) {//如果是尾部
last = last.prev;
} else {//删除的是中间
cur.next.prev = cur.prev;
}
}
return;
}
cur = cur.next;
}
}
1.通过遍历找到值等于key的结点
2.如果要删的是头结点,头结点向后移动一位。如果移动后的头结点不为空,将此时头结点的前驱置为空
3.如果要删除的cur是尾结点,将cur前驱的地址值指向cur的下一个地址,将last向前移动一位
4.如果要删除的是中间结点,将cur前驱的地址值指向cur的下一个地址,将cur后继的前驱指向cur的前驱
6.清空链表
java
public void clear() {
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.next = null;
cur.prev = null;
cur = curNext;
}
head = null;
last = null;
}
1.遍历链表,用curNext记录cur的下一个结点
2.将cur的前驱和后继置为null
3.将头结点和尾结点置为空。