为了提高大家的学习效率,我把代码放开头,供查阅。
目录
一、链表的实现代码
二、什么是链表
三、链表的分类
四、链表的常见操作
插入
删除
五、Java自带的LinkedList
两个构造方法
一些常用方法
六、LinkedList的遍历
七、ArrayList和LinkedList的区别
一、链表的实现代码
//无头单向链表
package demo1;
public class MySingleLinkedList{class ListNode{public int var;public ListNode next;public int val;public ListNode(int var) {this.var = var;}}ListNode head;public void creat(){ListNode node1=new ListNode(1);ListNode node2=new ListNode(1);ListNode node3=new ListNode(1);ListNode node4=new ListNode(1);node1.next=node2;node2.next=node3;node3.next=node4;head=node1;}//头插法public void addFirst(int data) {ListNode node=new ListNode(data);node.next=head;head=node;}//尾插法public void addLast(int data) {ListNode node=new ListNode(data);if(head==null){head=node;return;}ListNode cur=head;while (cur.next!=null){cur=cur.next;}cur.next=node;}private void IndexCheck(int index)throws IndexNotLegal {if(index<0||index>size()){throw new IndexNotLegal("index位置不合法");}}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index, int data){try {IndexCheck(index);}catch (IndexNotLegal e){e.printStackTrace();return;}ListNode node=new ListNode(data);ListNode cur=head;if(index==0){addFirst(data);return;}if(index==size()){addLast(data);return;}//注意这里是index-1for (int i = 0; i < index-1; i++) {cur= cur.next;}node.next=cur.next;cur.next=node;}//查找是否包含关键字key是否在单链表当中public boolean contains(int key) {ListNode cur=head;while (cur!=null){if(cur.var==key){return true;}cur=cur.next;}return false;}//删除第一次出现关键字为key的节点public void remove(int key) {if(head.var==key){head=head.next;return;}if(head==null){return;}ListNode cur=head;while (cur.next!=null){if(cur.next.var==key){cur.next=cur.next.next;return;}cur=cur.next;}}//删除所有出现的key节点public void removeAllKey(int key) {if(head==null){return;}ListNode prev=head;ListNode cur=head.next;while (cur!=null){if(cur.var==key){prev.next= cur.next;//这里不能写prev=curcur= cur.next;}else {prev=cur;cur= cur.next;}if(head.var==key){head=head.next;}}}//得到单链表的长度public int size() {ListNode cur=head;int count=0;while (cur!=null){count++;cur=cur.next;}return count;}//打印链表public void display() {ListNode cur=head;for (int i = 0; i < size(); i++) {System.out.print(cur.var+" ");cur=cur.next;}System.out.println();}//清除链表public void clear() {head=null;}
}public class IndexNotLegal extends RuntimeException {public IndexNotLegal(){}public IndexNotLegal(String s){super(s);}
}package demo2;
// 无头双向链表
public class MyLinkedList {class ListNode {public int var;ListNode prev;ListNode next;public ListNode(int var) {this.var = var;}}ListNode head;ListNode last;//头插法public void addFirst(int data) {ListNode node = new ListNode(data);if (head == null) {head = last = node;return;}node.next = head;head.prev = node;head = node;}//尾插法public void addLast(int data) {ListNode node = new ListNode(data);if (head == null) {head = last = node;return;}last.next = node;node.prev = last;last = node;}private void indexCheck(int index) throws IndexNotLegal {if (index < 0 || index > size()) {throw new IndexNotLegal("index位置不合法");}}private ListNode findIndexCur(int index) {ListNode cur = head;while (index != 0) {cur = cur.next;index--;}return cur;}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index, int data) {try {indexCheck(index);} catch (IndexNotLegal e) {e.printStackTrace();}if (index == 0) {addFirst(data);return;}if (index == size()) {addLast(data);return;}ListNode node = new ListNode(data);ListNode cur = findIndexCur(index);//先把node插进去,再调整node的前一个,最后再调cur,因为需要cur来调整node.next=cur;cur.prev.next=node;node.prev=cur.prev;cur.prev=node;}//查找是否包含关键字key是否在单链表当中public boolean contains(int key) {ListNode cur = head;while (cur != null) {if (cur.var == key) {return true;}cur = cur.next;}return false;}//删除第一次出现关键字为key的节点public void remove(int key) {ListNode cur = head;while (cur != null) {if (cur.var == key) {//头节点if (head.var == key) {head.next.prev = null;head = head.next;return;}//尾巴节点if (last.var == key) {last.prev.next = null;last = last.prev;return;}cur.next.prev = cur.prev;cur.prev.next = cur.next;return;}cur = cur.next;}}//删除所有值为key的节点public void removeAllKey(int key) {ListNode cur = head;while (cur != null) {if (cur.var == key) {//头节点if (head.var == key) {head.next.prev = null;head = head.next;return;}//尾巴节点if (last.var == key) {last.prev.next = null;last = last.prev;return;}cur.next.prev = cur.prev;cur.prev.next = cur.next;}cur = cur.next;}}//得到单链表的长度public int size() {int count = 0;ListNode cur = head;while (cur != null) {cur = cur.next;count++;}return count;}public void display() {ListNode cur = head;for (int i = 0; i < size(); i++) {System.out.print(cur.var + " ");cur = cur.next;}System.out.println();}public void clear() {head = last = null;}
}package demo2;public class IndexNotLegal extends RuntimeException{public IndexNotLegal(){}public IndexNotLegal(String s){super(s);}
}
二、什么是链表
简单来说,像链子一样的数据结构。像火车一节一节车厢一样,每个元素是独立的个体(内部类)。 并且他们在空间里是分散的。
为什么分散的还可以找到下一个呢?
答:一个节点里面装着两种东西,一个是值,一个的下一个的地址,这样根据下一个的地址就可以找到下一个了。
三、链表的分类
常见的链表有三种,但是我这里只介绍两种:无头单链,无头双链。剩下的一种之后再补充。
单链和双链的区别?
答: 一个节点都是一个类。单链装的是值和下个节点;双链装的是值和上个节点和下个节点
四、链表的常见操作
插入
怎么插入元素?在链表中很简单!
单链:p下个节点改成n1,n0下个节点改为p。
双链: p下个节点改为n1,n0下个节点改为p(先把p插进去,成一个逆时针),p上个节点改为n0,n1的上个节点改为p(修改原来的两点节点)
注意!还需要考虑一些特殊情况,具体请看代码的实现!
删除
在链表中删除节点也非常方便,只需改变一个节点的引用(指针)即可。
单链:n0的下个节点指向n1。
双链:n0下个节点改为n1,n1上个节点改为n0 。
问题来了:为什么p的指向n1可以不删?
答;原因是以及没人指向p了, 如果没人引用(指向)的东西,就会被系统自动回收。 像clear()方法,head不指向首个节点,那么首个节点被回收,同理下面也如此。
五、Java自带的LinkedList
两个构造方法
方法 | 解释 |
LinkedList() | 无参构造 |
public LinkedList(Collection c) | 使用其他集合容器中元素构造List |
public static void main(String[] args) {
// 构造一个空的LinkedListList<Integer> list1 = new LinkedList<>();List<String> list2 = new java.util.ArrayList<>();list2.add("JavaSE");list2.add("JavaWeb");list2.add("JavaEE");
// 使用ArrayList构造LinkedListList<String> list3 = new LinkedList<>(list2);
}
一些常用方法
方法 | 解释 |
boolean add(E e) | 尾插 e |
void add(int index, E element) | 将 e 插入到 index 位置 |
boolean addAll(Collection c) | 尾插 c 中的元素 |
E remove(int index) | 删除 index 位置元素 |
boolean remove(Object o) | 删除遇到的第一个 o |
E get(int index) | 获取下标 index 位置元素 |
E set(int index, E element) | 将下标 index 位置元素设置为 element |
void clear() | 清空 |
boolean contains(Object o) | 判断 o 是否在线性表中 |
int indexOf(Object o) | 返回第一个 o 所在下标 |
int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
List subList(int fromIndex, int toIndex) | 截取部分 list |
public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();list.add(1); // add(elem): 表示尾插list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());System.out.println(list);
// 在起始位置插入0list.add(0, 0); // add(index, elem): 在index位置插入元素elemSystem.out.println(list);list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()list.removeFirst(); // removeFirst(): 删除第一个元素list.removeLast(); // removeLast(): 删除最后元素list.remove(1); // remove(index): 删除index位置的元素System.out.println(list);
// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
if(!list.contains(1)){list.add(0, 1);
}list.add(1);System.out.println(list);System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置int elem = list.get(0); // get(index): 获取指定位置元素list.set(0, 100); // set(index, elem): 将index位置的元素设置为elemSystem.out.println(list);
// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回List<Integer> copy = list.subList(0, 3); System.out.println(list);System.out.println(copy);list.clear(); // 将list中元素清空System.out.println(list.size());
}
六、LinkedList的遍历
public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();list.add(1); // add(elem): 表示尾插list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());
//for循环遍历
for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i)+" ");
}
// foreach遍历
for (int e:list) {System.out.print(e + " ");
}System.out.println();
// 使用迭代器遍历---正向遍历ListIterator<Integer> it = list.listIterator();
while(it.hasNext()){System.out.print(it.next()+ " ");
}System.out.println();
// 使用反向迭代器---反向遍历ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){System.out.print(rit.previous() +" ");
}System.out.println();
}
七、ArrayList和LinkedList的区别
不同点 | 数组 | 链表 |
存储方式 | 连续内存空间 | 分散内存空间 |
容量扩展 | 长度不可变 | 可灵活扩展 |
内存效率 | 元素占用内存少、但可能浪费空间 | 元素占用内存多 |
访问元素 | O(1) | O(N) |
添加元素 | O(N) | O(1) |
删除元素 | O(N) | O(1) |