链表结构的每个节点汇包含一个数据域和一个引用域,引用域直接存放下一个节点的地址.
//链节点
public class Link {
private String data;
public Link next;
public Link(String data){
this.data = data;
}
public String getData(){
return this.data;
}
public void display(){
System.out.println("节点是"+data+",它的下一个节点是"+(null==next?"null":this.next.getData()));
}
}
//链表
public class LinkList {
private Link first; //最前节点
public LinkList(){
first = null;
}
//新增节点
public void insert(Link link){
link.next = first; //新节点的next指向first
first = link; //first变为新节点
System.out.print("插入新节点:");
link.display();
}
//获取节点,并未实际删除
public Link delete(){
Link result = null;
if(!isEmpty()){
result = first;
first = result.next;
}
if(null!=result){
System.out.print("删除的节点是:");
result.display();
}else{
System.out.println("已无节点可删除!!!");
}
return result;
}
//是否为空
public boolean isEmpty(){
return null==first;
}
//查找指定节点
public Link find(String key){
Link temp = first;
while(key!=temp.getData()){
temp = temp.next;
if(null == temp){
break;
}
}
if(null == temp){
System.out.println("未找到节点,返回null!");
}else{
System.out.print("找到的节点是:");
temp.display();
}
return temp;
}
//删除指定节点
public Link findAndDelete(String key){
Link cur = first;
Link pre = first;
while(key != cur.getData()){
if(null == cur.next){
//如果当前节点未匹配,下一个节点为空,则未查找到.
System.out.println("未找到节点!!!");
return null;
}else{
//pre为cur的上一个节点
pre = cur;
//cur变为下一个节点
cur = cur.next;
}
}
//将cur移除(直接将pre的下个节点指向cur的下一个节点)
pre.next = cur.next;
if(null!=cur){
System.out.print("删除的节点是:");
cur.display();
}else{
System.out.println("已无节点可删除!!!");
}
return cur;
}
//查找指定节点并删除
public static void main(String[] args) {
LinkList list = new LinkList();
list.insert(new Link("001"));
list.insert(new Link("002"));
list.insert(new Link("003"));
list.insert(new Link("004"));
list.insert(new Link("005"));
System.out.println("====================");
list.delete();
list.delete();
System.out.println("====================");
list.find("002");
System.out.println("====================");
list.findAndDelete("002");
System.out.println("====================");
list.delete();
list.delete();
}
}
输出:
插入新节点:节点是001,它的下一个节点是null
插入新节点:节点是002,它的下一个节点是001
插入新节点:节点是003,它的下一个节点是002
插入新节点:节点是004,它的下一个节点是003
插入新节点:节点是005,它的下一个节点是004
====================
删除的节点是:节点是005,它的下一个节点是004
删除的节点是:节点是004,它的下一个节点是003
====================
找到的节点是:节点是002,它的下一个节点是001
====================
删除的节点是:节点是002,它的下一个节点是001
====================
删除的节点是:节点是003,它的下一个节点是001
删除的节点是:节点是001,它的下一个节点是null
first和last初始都为空,两端都可以插入,只有一个节点时,既是first也是last.
//双端链表
public class FirstLastLinkList {
private Link first;
private Link last;
public FirstLastLinkList(){
first = null;
last = null;
}
//从头插入
public void insertFirst(Link link){
if(isEmpty()){ //链表为空时,first和last都是这个节点
last = link;
}
link.next = first; //不为空时,操作与last无关
first = link;
System.out.print("插入新节点:");
link.display();
}
//从尾插入
public void insertLast(Link link){
if(isEmpty()){ //链表为空时,first和last都是这个节点.直接赋值即可
first = link;
}else{
last.next = link; //不为空时,操作与first无关.
}
last = link;
System.out.print("插入新节点:");
link.display();
}
//从头删除一个
public Link deleteFirst(){
if(isEmpty()){
System.out.println("链表为空!!!");
return null;
}
Link temp = first;
if(null == first.next){ //如果只有一个元素,last也要置空
last = null;
}
first = first.next;
System.out.print("删除的节点是:");
temp.display();
return temp;
}
//判断是否为空
public boolean isEmpty(){
return null==first;
}
public static void main(String[] args) {
FirstLastLinkList list = new FirstLastLinkList();
list.insertFirst(new Link("001"));
list.insertFirst(new Link("002"));
list.insertFirst(new Link("003"));
list.insertLast(new Link("002"));
list.insertLast(new Link("001"));
list.deleteFirst();
list.deleteFirst();
list.deleteFirst();
list.deleteFirst();
list.deleteFirst();
}
}
输出:
插入新节点:节点是001,它的下一个节点是null
插入新节点:节点是002,它的下一个节点是001
插入新节点:节点是003,它的下一个节点是002
插入新节点:节点是002,它的下一个节点是null
插入新节点:节点是001,它的下一个节点是null
删除的节点是:节点是003,它的下一个节点是002
删除的节点是:节点是002,它的下一个节点是001
删除的节点是:节点是001,它的下一个节点是002
删除的节点是:节点是002,它的下一个节点是001
删除的节点是:节点是001,它的下一个节点是null
链表的效率
插入和删除 O(1)
查找O(N)
链表的优点
和数组相比,删除操作不需要节点的移动,效率有一定的提升.
另一个优点,链表的内存利用率高,有几个节点就占用多少内存,不需要指定节点数据.而数组需要预先开辟出一定长度的内存.虽然数组支持动态扩展,仍然是按比例的,比如长度为10扩展到20.
链表的缺点
链表相对数组的缺点:随机查找不方便.数组可以根据角标随机取出一个位置的数.而链表无法这么做,只能从头开始遍历.
链表的用途
普通链表实现栈,从first插入,从first取出即可,且理论上长度不限.
双端链表实现队列,从last插入,从first取出.很好地避免了用数组实现时的指针回绕问题.
有序链表可以用于实现优先级队列.
节点在链表中依大小有序排列.
有序链表的优缺点
有序列表相对于有序数组的优点:
插入,删除效率较高,不用移动节点
内存利用率高.
有序链表的效率
删除O(1) 插入O(N)
有序链表的用途
有序链表可以用于实现优先级队列
有序链表可以用于数组的排序: 以插入排序为例,时间复杂度为O(N的平方).
//链节点
public class Link {
private int data;
public Link next;
public Link(int data){
this.data = data;
}
public int getData(){
return this.data;
}
public void display(){
System.out.println("节点是"+data+",它的下一个节点是"+(null==next?"null":this.next.getData()));
}
}
//有序链表
public class SortedLinkList {
private Link first;
public SortedLinkList(){
this.first = null;
}
//插入时进行排序
public void insert(Link link){
Link pre = null;
Link cur = first;
while(cur!=null && link.getData() <= cur.getData()){
pre = cur;
cur = cur.next;
}
if(null == pre){ //如果cur没有移动
first = link;
}else{
pre.next = link;
}
link.next = cur;
System.out.println("插入节点:"+link.getData());
}
//删除一般只删除表头节点
public Link remove(){
Link temp = first;
if(null!=first){
first = first.next;
}
System.out.println("取出节点:"+(null==temp?"null":temp.getData()));
return temp;
}
public static void main(String[] args) throws Exception{
SortedLinkList list = new SortedLinkList();
list.insert(new Link(20));
list.insert(new Link(70));
list.insert(new Link(60));
list.remove();
list.remove();
list.remove();
}
}
输出:
插入节点:20
插入节点:70
插入节点:60
取出节点:70
取出节点:60
取出节点:20
双向链表节点中有两个引用域,分别指向上一个节点和下一个节点,双向链表也可以是双端链表.
如图所示:
//链表
public class Link {
private int data;
public Link next;
public Link pre;
public Link(int data){
this.data = data;
}
public int getData(){
return this.data;
}
public void display(){
System.out.println("节点是"+data+",它的下一个节点是"+(null==next?"null":this.next.getData())+",它的上一个节点是"+(null==pre?"null":this.pre.getData()));
}
}
//双向链表
public class DoublyLinkedList {
private Link first;
private Link last;
public DoublyLinkedList(){
first = null;
last = null;
}
//判断是否为空
public boolean isEmpty(){
return null == first;
}
//在头部插入
public void insertFirst(Link link){
if(isEmpty()){
last = link;
}else{
first.pre = link;
link.next = first;
}
System.out.print("节点被增加:");
link.display();
first = link;
}
//在尾部插入
public void insertLast(Link link){
if(isEmpty()){
first = link;
}else{
last.next = link;
link.pre = last;
}
System.out.print("节点被增加:");
link.display();
last = link;
}
//在头部删除
public Link deleteFirst(){
if(isEmpty()){ //如果为空
System.out.println("链表已为空!!!");
return null;
}
Link temp = first;
if(null == first.next){ //如果只有一个节点
last = null;
}else{
first.next.pre = null;//删除第一个节点前先把第二个节点的前节点置空
}
first = first.next;
System.out.print("节点被删除:");
temp.display();
return temp;
}
//在尾部删除
public Link deleteLast(){
if(isEmpty()){
System.out.println("链表已为空!!!");
return null;
}
Link temp = last;
if(null == first.next){ //只有一个节点
first = null;
}else{
last.pre.next = null; //删除最后一个节点先将倒数第二个节点的后节点置空
}
last = last.pre;
System.out.print("节点被删除:");
temp.display();
return temp;
}
public static void main(String[] args) {
DoublyLinkedList list = new DoublyLinkedList();
list.insertFirst(new Link(10));
list.insertFirst(new Link(20));
list.insertLast(new Link(40));
list.insertLast(new Link(30));
list.deleteFirst();
list.deleteFirst();
list.deleteLast();
list.deleteLast();
}
}
输出:
节点被增加:节点是10,它的下一个节点是null,它的上一个节点是null
节点被增加:节点是20,它的下一个节点是10,它的上一个节点是null
节点被增加:节点是40,它的下一个节点是null,它的上一个节点是10
节点被增加:节点是30,它的下一个节点是null,它的上一个节点是40
节点被删除:节点是20,它的下一个节点是10,它的上一个节点是null
节点被删除:节点是10,它的下一个节点是40,它的上一个节点是null
节点被删除:节点是30,它的下一个节点是null,它的上一个节点是40
节点被删除:节点是40,它的下一个节点是null,它的上一个节点是null
将集合的和遍历相关操作委托给迭代器,通过迭代器中指针的移动进行相关操作.
public class Link {
private int data;
public Link next;
public Link(int data){
this.data = data;
}
public void display(){
System.out.print(data+">");
}
}
public class LinkList {
private Link first;
public LinkList(){
first = null;
}
//获取第一个节点
public Link getFirst(){
return first;
}
//设置第一个节点
public void setFirst(Link f){
first = f;
}
//判空
public boolean isEmpty(){
return null == first;
}
//获取迭代器
public ListIterator getIterator(){
return new ListIterator(this);
}
//展示list
public void displayList(){
Link cur = first;
while(null != cur){
cur.display();
cur = cur.next;
}
System.out.println();
}
}
//迭代器的主要作用是将集合的一些遍历操作委托给迭代器
public class ListIterator {
private Link cur;
private Link pre;
private LinkList list;
public ListIterator(LinkList list){
this.list = list;
reset();
}
//重置迭代器
public void reset(){
cur = list.getFirst();
pre = null;
}
//判断是否在末尾
public boolean atEnd(){
return null == cur.next;
}
//指针移动到下一个
public void goNext(){
pre = cur;
cur = cur.next;
}
//获取当前指针
public Link getCur(){
return cur;
}
//在当前指针后插入
public void insertAfterCur(Link newLink){
//如果list为空
if(list.isEmpty()){
list.setFirst(newLink);
cur = newLink;
}else{
newLink.next = cur.next;
cur.next = newLink;
goNext();
}
}
//在当前指针前插入
public void insertBeforeCur(Link newLink){
if(null == pre){ //指针在链头
pre.next = list.getFirst();
list.setFirst(newLink);
reset();
}else{
newLink.next = pre.next;
pre.next = newLink;
cur = newLink;
}
}
//获取当前指针
public Link deleteCur(){
Link temp = cur;
if(list.isEmpty()){
System.out.println("链表为空!!!");
return null;
}
if(null == pre){//指针在链头
list.setFirst(cur.next);
reset();
}else{
pre.next = cur.next;
if(atEnd()){ //指针在链尾,直接移到链头
reset();
}else{
cur = cur.next;
}
}
return temp;
}
public static void main(String[] args) {
LinkList list = new LinkList();
ListIterator iterator = list.getIterator();
iterator.insertAfterCur(new Link(10));
iterator.insertAfterCur(new Link(20));
iterator.insertBeforeCur(new Link(30));
iterator.insertBeforeCur(new Link(40));
list.displayList();
iterator.deleteCur();
list.displayList();
}
}
输出:
10>40>30>20>
10>30>20>