目录
1.链表
1.1链表的定义和分类
定义:链表在物理存储上非连续,逻辑上连续。数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
分类:8种:
单向 带头 循环 双向 带头 循环
单向 带头 非循环 双向 带头 非循环
单向 不带头 循环 双向 不带头 循环
单向 不带头 非循环 双向 不带头 非循环 (LinkedList的底层)
带头与不带头:带头的头一直都是head,后面插入新数据头不变
public class MySingleList {
class ListNode {//内部类
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
private ListNode head;//head是链表的属性,不是节点的属性
}
1.2常见方法的实现
public class MySingleList {
class ListNode {//内部类,只在该外部类内部使用
public int val;//存值
public ListNode next;//存地址
public ListNode(int val) {
this.val = val;
}
}
private ListNode head;//head是链表的属性,不是节点的属性
//创建链表
public void createList() { //有了下面的addFirst方法后这个方法就淘汰了
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
ListNode node5 = new ListNode(5);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
this.head = node1;
}
//遍历打印链表
public void show() {
ListNode cur = head;//这里只是定义了一个变量,不是new了一个新的对象,这样可以不改变head的指向
while (cur != null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
//得到单链表的长度---链表中节点的个数
public int size(){
ListNode cur = head;
int count = 0;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
//查找链表中是否包含关键字key
public boolean contains(int key){
ListNode cur = head;
while (cur != null) {
if (cur.val == key) { //如果val值为引用类型,要用equals来比较
return true;
}
cur = cur.next;
}
return false;
}
// 头插法
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;//node直接为头
return;
}
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
//找到index-1位置的节点
private ListNode findIndex(int index) {
ListNode cur = head;
while (index-1 > 0) {
cur = cur.next;
index--;
}
return cur;
}
//找到元素key所在的节点
private ListNode findcur(int key) {
ListNode cru = head;
while (cru.next != null) {
if (cru.next.val == key) {
return cru;
}
else {
cru = cru.next;
}
}
return null;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
if (index < 0 || index > size()) {
throw new IndexOutOfBoundsException("index的位置不合法");
}
if (index == 0) { //头插
addFirst(data);
return;
}
if (index == size()) { //尾插
addLast(data);
return;
}else {
ListNode node = new ListNode(data);
ListNode cur = findIndex(index);
node.next = cur.next;
cur.next = node;
}
}
//删除第一次出现关键字为key的节点
public void remove(int key){
if (head == null) {
return;
}
if (head.val == key) {
head = head.next;
return;
}
ListNode cur = findcur(key);
if (cur == null) {
System.out.println("没有你要删除的数据");
return;
}
ListNode del = cur.next;
cur.next = del.next;
}
//删除所有值为key的节点
public void removeAllKey(int key){ //双指针
if (head == null) {
return;
}
while (head.val == key) { //或使用if循环放在最后
head = head.next;
}
ListNode cur = head;//代表当前要删除的节点
ListNode prev = head;//代表当前节点的前驱节点
while (cur != null) {
if (cur.val == key) {
prev.next = cur.next;
cur = cur.next;
}else {
prev = cur;
cur = cur.next;
}
}
}
//回收所有数据
public void clear() {
//this.head = null;//只需将头结点置为null即可
while (head != null) { //或者将每个节点置为null
ListNode headNext = head.next;
head.next = null;
head = headNext;
}
}
public static void main(String[] args) {
MySingleList mySingleList = new MySingleList();
mySingleList.createList();
mySingleList.show();//1 2 3 4 5
System.out.println(mySingleList.size());//5
System.out.println(mySingleList.contains(4));//true
System.out.println(mySingleList.contains(7));//false
mySingleList.addFirst(8);
mySingleList.addFirst(9);
mySingleList.show();//9 8 1 2 3 4 5
mySingleList.addLast(10);
mySingleList.addLast(11);
mySingleList.show();//9 8 1 2 3 4 5 10 11
mySingleList.addIndex(3,12);
mySingleList.addIndex(6,13);
mySingleList.show();//9 8 1 12 2 3 13 4 5 10 11
mySingleList.remove(13);
mySingleList.remove(18);//没有你要删除的数据
mySingleList.show();//9 8 1 12 2 3 4 5 10 11
mySingleList.addFirst(10);
mySingleList.removeAllKey(10);
mySingleList.show();//9 8 1 12 2 3 4 5 11
mySingleList.clear();
mySingleList.show();//啥都没了
}
}
1.3链表面试题
十道链表面试题,快来试试!! 链表面试题
2.LinkedList
1> LinkedList实现了List接口;
2> LinkedList的底层使用了双向链表;
3> LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问;
4> LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1);
5> LinkedList比较适合任意位置插入的场景;
2.1双向链表
2.2双向链表的模拟实现
public class MyLinkedList {
static class ListNode {
public int val;
public ListNode prev;//默认为null
public ListNode next;//默认为null
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;//头
public ListNode last;//尾
//头插法
public void addFirst(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
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 = node;
last = node;
return;
}
last.next = node;
node.prev = last;
last = node;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data) {
if (index < 0 || index > size()) {
throw new IndexOutOfBoundsException("index不合法");
}
if (index == 0) {
addFirst(data);
return;
}
if (index == size()) {
addLast(data);
return;
}
//找到index所在的节点
ListNode cur = head;
while (index != 0) {
cur = cur.next;
index--;
}
//开始插入
ListNode node = new ListNode(data);
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.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
//删除第一次出现关键字为key的节点
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;
}
}
if (cur == last) { //删除尾节点
cur.prev.next = null;
last = last.prev;
}else { //其他情况
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
}
return;
}
cur = cur.next;
}
}
//删除所有值为key的节点
public void removeAllKey(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val == key) {
if (cur == head) {
head = head.next;
if (head != null) {
head.prev = null;
}else {
last = null;
}
}
if (cur == last) {
cur.prev.next = null;
last = last.prev;
}else {
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
}
}
cur = cur.next;//同上,只是没有return而是一直向后走
}
}
//得到双链表的长度
public int size() {
int len = 0;
ListNode cur = head;
while (cur != null) {
cur = cur.next;
len++;
}
return len;
}
//打印双链表
public void display() {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
//清空双链表
public void clear() {
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.prev = null;
cur.next = null;
//cur.val = null;//如果val是引用类型
cur = curNext;
}
head = null;
last = null;
}
}
2.3LinkedList常见方法
与ArrayList几乎一毛一样,这里就不赘述了,详见 ArrayList与顺序表
2.4LinkedList的遍历
public class Test {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1); //尾插
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
System.out.println(list.size());//6
// foreach遍历
for (int x:list) {
System.out.print(x + " ");
}
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();
}
}
3.ArrayList与LinkedList的区别
不同点 | ArrayList | LinkedList |
---|---|---|
存储空间 | 物理上连续 | 逻辑上连续,物理上不一定连续 |
随机访问 | 支持 | 不支持 |
头插 | O (n) | O (1) |
插入 | 空间不够时需要扩容 | 没有容量这一概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入+频繁删除 |
链表比顺序表难不少啊,冲冲冲!!