为了提高大家的学习效率,我把代码放开头,供查阅。
目录
一、链表的实现代码
二、什么是链表
三、链表的分类
四、链表的常见操作
插入
删除
五、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-1
for (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=cur
cur= 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) {
// 构造一个空的LinkedList
List<Integer> list1 = new LinkedList<>();
List<String> list2 = new java.util.ArrayList<>();
list2.add("JavaSE");
list2.add("JavaWeb");
list2.add("JavaEE");
// 使用ArrayList构造LinkedList
List<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);
// 在起始位置插入0
list.add(0, 0); // add(index, elem): 在index位置插入元素elem
System.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位置的元素设置为elem
System.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) |