链表的概念:
链式结构 在逻辑上连续 在物理上不一定连续
现实中节点都是从堆上申请出来的
从堆上申请的空间,按照一定策略分配,两次申请的空间可能连续,也可能不连续。
结构:
单向 双向
带头节点 (head不会变,而我们后面用的那不叫带头结点,叫head头指针)不带头节点
循环 不循环
无头单向非循环链表的实现
MySingleLinkedList
public class MySingleLinkedList {//链表
//首先创建一个静态内部类来描述节点
static class ListNode {//静态内部类,List是由ListNode组成的,它有哪些属性
public int val;//节点的值域
public ListNode next;//next域,因为他是引用类型,存放的是下一个节点的地址
public ListNode(int val) {
this.val = val;
//value域构造方法可以放一个初始值,而next域没有构造就是null,因为一开始也不知道我的下一个节点是谁。
}
}
public ListNode head;//节点引用类型 表示当前链表的头节点 链表的成员属性,不是节点的属性
public int usedSize;//记录链表里的节点个数
public void createList() {//创建链表
ListNode node1 = new ListNode(12);//创建节点
ListNode node2 = new ListNode(23);
ListNode node3 = new ListNode(34);
ListNode node4 = new ListNode(45);
ListNode node5 = new ListNode(56);
node1.next = node2;//node1的next域存放的是node2的地址,相当于将node1和node2连接起来了。
node2.next = node3;
node3.next = node4;
node4.next = node5;
this.head = node1;//注意:得写成head=node1,不能写成node1=head
}
public void display(){
ListNode cur = head;
while (cur != null) {//cur == null表示把链表遍历完了
System.out.print(cur.val + " ");
// head = head.next;不用head,不然到时候找不到头节点去哪儿了
cur = cur.next;//每次向后走一步
}
}
public int size(){//得到链表的长度
ListNode cur = head;
int count = 0;
while (cur != null){
count++;
cur = cur.next;
}
return count;
}
public boolean contains(int key){//查找是否包含key
ListNode cur = head;
while (cur != null) {//cur 而不是cur.next
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
public void addFirst(int data) {
//头插法:这里不需要考虑链表是否为空
ListNode node = new ListNode(data);
node.next = head;//这两行代码不可交换
head = node;
}
//cur == null表示遍历完了每一个节点
//cur.next == null 表示cur是最后一个节点的位置
public void addEnd(int data) {
ListNode node = new ListNode(data);
ListNode cur = head;
if(cur == null) {//得判断,不然cur没有next,报空指针异常,空链表
head = node;
return;//要有这句,不然还会往后执行
}
while (cur.next != null) {//尾巴节点的next域不为空
cur = cur.next;
}
cur.next = node;
}
public void addIndex(int index, int data){
if(index < 0 || index > size()) {
System.out.println("index不合法");//可以抛出一个自定义异常
return;
}
if(index == 0) {
addFirst(data);//头插
return;
}
if(index == size()) {
addEnd(data);//尾插
return;
}
ListNode node = new ListNode(data);
ListNode cur = findIndexSubOne(index);//找到要插入位置的前一个节点
node.next = cur.next;
cur.next = node;
}
private ListNode findIndexSubOne(int index) {//封装
ListNode cur = head;
while (index-1 != 0) {
cur = cur.next;
index--;
}
return cur;
}
private ListNode searchPrev(int key) {//找到你要删除节点的前一个节点
ListNode cur = head;
while(cur.next != null) {//不会到最后一个节点,最多到最后一个节点的前一个节点
if(cur.next.val == key) {
return cur;
}
cur = cur.next;
}
return null;//一个都没找到
}
//删除第一个出现关键字为key的节点
public void remove(int key){//1.找到你要删除节点的前一个节点
if(head == null) {//空链表 //得考虑
return;
}
if(head.val == key) { //得考虑
//单独删除头节点
head = head.next;
}
ListNode cur = searchPrev(key);
if(cur == null) {
System.out.println("没找到你要删除的数字");
return;
}
ListNode del = cur.next;
cur.next = del.next;
}
public void removeAllKey(int key) {
if(head == null) {
return;
}
ListNode cur = head.next;
ListNode prev = head;
while(cur != null) {
if(cur.val == key){
prev.next = cur.next;
cur = cur.next;
}else {
prev = cur;
cur = cur.next;
}
}
if(head.val == key) {//如果头节点为key
head = head.next;//不能放在一开始,因为有可能前面几个都是key,放前面必须用循环。
}
}
public void clear(){
this.head = null;
}
}
Test类
public static void main(String[] args) {
MySingleLinkedList mySingleLinkedList = new MySingleLinkedList();//实例化一个对象,
mySingleLinkedList.createList();
mySingleLinkedList.display();
System.out.println(mySingleLinkedList.size());
mySingleLinkedList.createList();
System.out.println(mySingleLinkedList.contains(23));
System.out.println(mySingleLinkedList.contains(99));
mySingleLinkedList.addFirst(12);
mySingleLinkedList.addFirst(9);
mySingleLinkedList.addFirst(8);
mySingleLinkedList.display();
mySingleLinkedList.addEnd(19);
mySingleLinkedList.addEnd(20);
mySingleLinkedList.display();
mySingleLinkedList.createList();
mySingleLinkedList.addIndex(0,100);
mySingleLinkedList.addIndex(5,200);
mySingleLinkedList.addIndex(2,300);
mySingleLinkedList.display();
mySingleLinkedList.createList();
mySingleLinkedList.remove(23);
mySingleLinkedList.remove(34);
mySingleLinkedList.display();
}
LinkedList的模拟实现( 无头双向链表)
MyLinkedList
public class MyLinkedList {
static class ListNode{//静态内部类
private int val;
private ListNode prev;
private ListNode next;
public ListNode(int val){
this.val = val;
}
}
public ListNode head;//双向链表的头节点
public ListNode last;//双向链表的尾巴节点
public int size(){//得到链表长度
ListNode cur = head;
int count = 0;
while(cur != null) {
count++;
cur = cur.next;
}
return count;
}
public void display(){
ListNode cur = head;
while(cur != null) {
System.out.println(cur.val + " ");
cur = cur.next;
}
}
public boolean contains(int key) {//查找链表中是否包含关键字key
ListNode cur = head;
while(cur != null) {
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
//头插法
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;
}
}
//尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
last = node;
}
last.next = node;
node.prev = last;
last = last.next;
}
//固定位置插入
public void addIndex(int pos, int data){
checkIndex(pos);
if(pos == 0) {
addFirst(data);
return;
}
if(pos == size()) {
addLast(data);
return;
}
ListNode node = new ListNode(data);
ListNode cur = searchIndex(pos);
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
private void checkIndex(int index) {//判断位置的合法性
if(index < 0 || index > size()) {
throw new IndexOutOfException("index不合法!" + index);
}
}
private ListNode searchIndex(int index){//找到index位置的节点
ListNode cur = head;
while(index != 0) {
cur = cur.next;
index--;
}
return cur;
}
//删除第一次出现关键字为key的节点
public void remove(int key) {
ListNode cur = head;
while(cur != null){
if(cur.val == key){
if(cur == head) {//1.处理头节点
head = head.next;
if(head != null) {
删除之后呢,还剩一个节点
head.prev = null;
}else {
//删除之后没有节点了
last = null;
}
}else {
//2.处理中间节点
if(cur.next != null) {
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
}else {
//3.处理尾巴节点
cur.prev.next = cur.next;//将前一个节点的next置为null
last = last.prev;
}
}
return;//不写这个会造成空指针异常 删完这个之后就走人
}else {//如果节点的值不是key,继续往下走
cur = cur.next;
}
}
}
//删除所有值为key的节点
public void removeAllKey(int key) {
ListNode cur = head;
while(cur != null){
if(cur.val == key) {
if (cur == head) {//1.处理头节点
head = head.next;
if (head != null) {
删除之后呢,还剩一个节点
head.prev = null;
} else {
//删除之后没有节点了
last = null;
}
} else {
//2.处理中间节点
if (cur.next != null) {
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
} else {
//3.处理尾巴节点
cur.prev.next = cur.next;//将前一个节点的next置为null
last = last.prev;
}
}
}
cur = cur.next;
}
}
//清空链表
public void clear(){
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.prev = null;
cur.next = null;
cur = curNext;
}
head = null;//这两个引用还在,得置空
last = null;
}
}
Test类
public static void main(String[] args) {
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addFirst(10);
myLinkedList.addFirst(20);
myLinkedList.addFirst(20);
myLinkedList.addFirst(20);
myLinkedList.addFirst(50);
myLinkedList.addFirst(60);
myLinkedList.addLast(90);
myLinkedList.addLast(70);
myLinkedList.addLast(80);
myLinkedList.remove(10);
myLinkedList.remove(80);
myLinkedList.removeAllKey(20);
myLinkedList.display();
// myLinkedList.remove(10);
// myLinkedList.remove(60);
// myLinkedList.removeAllKey(20);
// myLinkedList.clear();
// myLinkedList.display();
}
异常类
public class IndexOutOfException extends RuntimeException{
public IndexOutOfException() {
}
public IndexOutOfException(String message) {
super(message);
}
}
LinkedList的使用
底层是双向链表,
适合任意位置插入和删除,时间复杂度为O(1)
未实现RandonAccess接口,不支持随机访问
public class Test{
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();//构造一个空的LinkedList
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
// System.out.println(list.size());//还有多少个有效元素
// System.out.println(list);
// list.add(20);//尾插
// list.add(1,50);//指定位置插入
// System.out.println(list);
// list.remove(2);//删除2位置上的元素
// list.remove();//删除第一个元素
// list.removeFirst();//删除第一个元素
// list.removeLast();//删除最后一个元素
// System.out.println(list);
// System.out.println(list.contains(30));//是否包含30?
// System.out.println(list.indexOf(30));//从前往后数30的位置在哪里
// System.out.println(list.lastIndexOf(30));//从后往前数30的位置在哪里
// int ret = list.get(0);//获取0位置上的元素
// System.out.println(ret);
//
// list.set(0,100);//将0位置设置为100
// int ret1 = list.get(0);
// System.out.println(ret1);
// List<Integer> list1 = list.subList(2,5);//左闭右开[2,5)
// System.out.println(list1);
// list.clear();
// System.out.println(list.size());
// List<Integer> list = new LinkedList<>();
// List<String> list1 = new ArrayList<>();
// list1.add("free solo");
// list1.add("climb");
//
// List<String> list2 = new LinkedList<>(list1);//用ArrayList构造LinkedList
}
}
LinkedList的遍历
//1.使用foreach遍历
for (int e: list) {
System.out.print(e + " ");
}
System.out.println();
//2.使用迭代器遍历
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
//从后往前遍历
ListIterator<Integer> it1 = list.listIterator(list.size());
while (it.hasPrevious()){
System.out.print(it.previous() + " ");
}