链表概念及组成
- 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的。
- 链表组成图示
- 单向链表
- 双向链表
链表特点
- 获取数据麻烦,需要遍历查找,比数组慢
- 方便插入、删除
链表实现
- 头结点为空结点
- 尾结点的next指向null
- 添加结点是在尾结点之后添加
- 插入结点图示(单向链表为例):
- 删除结点图示(单项链表为例):
- 其他操作基于以上两个操作,双向链表可类比
- 详细操作可查阅以下实现代码
单向链表实例
主要实现的是单向链表的反向
结点Node类:
- 为方便在链表中使用结点和简化代码,Node类中的属性定义为共有成员。
package com.Mylinklist;
public class Node {
public Object e;
public Node next;
public Node(){
}
public Node(Object e){
this.e=e;
next=null;
}
}
链表LinkList类:
- 方法不多,主要为了体现单向链表反向的方法,包括插入和删除方法
package com.Mylinklist;
import org.omg.CORBA.OBJ_ADAPTER;
public class LinkList {
private Node head;
private Node tail;
private int size=0;
/**
* 构造函数
*/
public LinkList (){
head=new Node();
tail=head;
}
/**
* 获得链表大小
*/
public int getsize(){
return size;
}
/**
*
* add添加元素
* 从链表尾部添加
*/
public void add(Object e){
Node n=new Node(e);
tail.next=n;
tail=n;
size++;
}
/**
* 通过索引查找
* 获取数据对象
*/
public Object gete(int index){
Node n=head.next;
for(int i=0;i<index;i++)
n=n.next;
return n.e;
}
/**
* 插入结点
* @param index 插入的位置
* @param e
*/
public void insert(int index,Object e){
if(index<0||index>size){
System.out.println("插入错误");
return;
}
Node newnode=new Node(e);
Node n=head.next;
//处理头结点
if(index==0){
head.next=newnode;
newnode.next=n;
//处理size为0的情况
if(size==0){
tail=newnode;
tail.next=null;
}
size++;
return ;
}
//取index索引的上一个节点
for(int i=0;i<index-1;i++){
n=n.next;
}
Node orn=n.next;
n.next=newnode;
newnode.next=orn;
//处理尾节点
if(index==size){
tail=newnode;
}
size++;
}
/**
* 删除结点
* @param index删除结点的位置
* @return
*/
public Object delete(int index){
if(index<0||index>=size){
return null ;
}
Node n=head.next;
//处理头结点
if(index==0){
Node n0=n;
n=n.next;
head.next=n;
n0.next=null;
//处理只有1个元素的链表
if(size==1)
tail=head;
size--;
return n0.e;
}
//取index前一个元素
for(int i=0;i<index-1;i++){
n=n.next;
}
Node n0=n.next;//当前数据
n.next=n0.next;
n0.next=null;
//处理尾结点
if(index==size-1){
tail=n;
}
size--;
return n0.e;
}
public LinkList reverse(){
LinkList list=new LinkList();
for(int i=size-1;i>=0;i--){
list.add(this.gete(i));
}
return list;
}
}
学生Student类型:
package com.MyArrayList;
public class Student {
public int score;
protected char sex;
int age;
private String name;
public Student(int score, char sex, int age, String name) {
this.score = score;
this.sex = sex;
this.age = age;
this.name = name;
}
public String toString() {
return "姓名:" + name + "\t年龄:" + age + "\t性别:" + sex + "\t学分:" + score;
}
}
主函数manage类:
- 测试插入,删除方法
- 测试反向方法
package com.Mylinklist;
import java.util.Random;
import com.MyArrayList.Student;
public class Manage {
public static void main(String [] args){
LinkList l=new LinkList();
Random rand=new Random();
int size=rand.nextInt(20);
System.out.println("要添加的元素个数为:"+size);
for(int i=0;i<size;i++){
Student stu=new Student(i*3%10,i%2==0? '男':'女',(i+10)%20,"name"+((char)('A'+i)));
l.add(stu);
}
System.out.println("数组队列中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.gete(i);
System.out.println(stu.toString());
}
System.out.println("索引4中插入一个学生:");
Student s=new Student(99,'女',18,"mmm");
l.insert(4, s);
System.out.println(s.toString());
System.out.println("数组队列中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.gete(i);
System.out.println(stu.toString());
}
System.out.println("删除一个学生:");
Student a=(Student) l.delete(5);
System.out.println(a.toString());
System.out.println("数组队列中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.gete(i);
System.out.println(stu.toString());
}
LinkList newl=l.reverse();
System.out.println("数组队列中存储的元素总数为:" + l.getsize());
for (int i = 0; i < newl.getsize(); i++) {
Student stu = (Student ) newl.gete(i);
System.out.println(stu.toString());
}
}
}
测试样例输出:
要添加的元素个数为:9
数组队列中存储的元素总数为:9
姓名:nameA 年龄:10 性别:男 r学分:0
姓名:nameB 年龄:11 性别:女 r学分:3
姓名:nameC 年龄:12 性别:男 r学分:6
姓名:nameD 年龄:13 性别:女 r学分:9
姓名:nameE 年龄:14 性别:男 r学分:2
姓名:nameF 年龄:15 性别:女 r学分:5
姓名:nameG 年龄:16 性别:男 r学分:8
姓名:nameH 年龄:17 性别:女 r学分:1
姓名:nameI 年龄:18 性别:男 r学分:4
索引4中插入一个学生:
姓名:mmm 年龄:18 性别:女 r学分:99
数组队列中存储的元素总数为:10
姓名:nameA 年龄:10 性别:男 r学分:0
姓名:nameB 年龄:11 性别:女 r学分:3
姓名:nameC 年龄:12 性别:男 r学分:6
姓名:nameD 年龄:13 性别:女 r学分:9
姓名:mmm 年龄:18 性别:女 r学分:99
姓名:nameE 年龄:14 性别:男 r学分:2
姓名:nameF 年龄:15 性别:女 r学分:5
姓名:nameG 年龄:16 性别:男 r学分:8
姓名:nameH 年龄:17 性别:女 r学分:1
姓名:nameI 年龄:18 性别:男 r学分:4
删除一个学生:
姓名:nameE 年龄:14 性别:男 r学分:2
数组队列中存储的元素总数为:9
姓名:nameA 年龄:10 性别:男 r学分:0
姓名:nameB 年龄:11 性别:女 r学分:3
姓名:nameC 年龄:12 性别:男 r学分:6
姓名:nameD 年龄:13 性别:女 r学分:9
姓名:mmm 年龄:18 性别:女 r学分:99
姓名:nameF 年龄:15 性别:女 r学分:5
姓名:nameG 年龄:16 性别:男 r学分:8
姓名:nameH 年龄:17 性别:女 r学分:1
姓名:nameI 年龄:18 性别:男 r学分:4
数组队列中存储的元素总数为:9
姓名:nameI 年龄:18 性别:男 r学分:4
姓名:nameH 年龄:17 性别:女 r学分:1
姓名:nameG 年龄:16 性别:男 r学分:8
姓名:nameF 年龄:15 性别:女 r学分:5
姓名:mmm 年龄:18 性别:女 r学分:99
姓名:nameD 年龄:13 性别:女 r学分:9
姓名:nameC 年龄:12 性别:男 r学分:6
姓名:nameB 年龄:11 性别:女 r学分:3
姓名:nameA 年龄:10 性别:男 r学分:0
双向链表实例
- 结点
package com.DoubleLinklist;
public class Node {
private Object e;
private Node next,last;
/**
* 无参构造
*/
public Node(){
next=null;
last=null;
}
/**
* 成员的基本操作
* @param n
*/
public void setNext(Node n){
next=n;
}
public void setLast(Node l){
last=l;
}
public Node getNext(){
return next;
}
public Node getLast(){
return last;
}
public Object getobject(){
return e;
}
/**
* 实例化构造
* @param e
*/
public Node(Object e){
this.e=e;
next=null;
last=null;
}
}
- 双向链表
package com.DoubleLinklist;
import com.DoubleLinklist.Node;
public class DoubleLink {
private Node front;
private Node tail;
private int size=0;
/**
* 无参构造
*/
public DoubleLink(){
front=new Node();
tail =front;
}
/**
* 获得链表大小
*/
public int getsize(){
return size;
}
/**
*
* add添加元素
* 从链表尾部添加
* 新结点上一个的指针指向tail
* tail下一个的指针指向新结点
* 把n赋值给tail
*/
public void add(Object e){
Node n=new Node(e);
tail.setNext(n);
n.setLast(tail);
tail=n;
size++;
}
/**
* 通过索引查找
* 获取数据对象
*/
public Object getObject(int index){
if(index<0||index>=size){
System.out.println("查找错误");
return null;
}
Node n=this.getNode(index);
Object e=n.getobject();
return e;
}
/**
* 获取索引结点
* @param index
* @return
*/
public Node getNode(int index){
if(index<0||index>=size){
System.out.println("查找错误");
return null;
}
Node n=front.getNext();
for(int i=0;i<index;i++)
n=n.getNext();
return n;
}
public int getIndex(Object e){
Node n=front.getNext();
for(int i=0;i<size-1;i++){
if(n.getobject()==e)
return i;
n=n.getNext();
}
return -1;
}
/**
* 插入结点
* @param e
*/
public void insert (int index,Object e){
if(index<0||index>size){
System.out.println("插入错误");
return ;
}
Node newnode=new Node(e);
Node n=this.getNode(index);
//先建立新关系,再断开原来的关系
newnode.setLast(n.getLast());
newnode.setNext(n);
n.getLast().setNext(newnode);
n.setLast(newnode);
/**
* 处理空链表
*/
if(size==0||index==size){
tail=newnode;
size++;
return;
}
size++;
//
}
/**
* 删除结点
* @param index
* @return
*/
public Object remove(int index){
if(index<0||index>=size){
System.out.println("没有这个元素");
return null;
}
Node n=this.getNode(index);
/**
*可单独处理size=1时的情况
*测试通过
*/
if(size==1){
front.setNext(null);
tail=front;
size--;
return n;
}
/**
* 正常情况下
*/
n.getLast().setNext(n.getNext());
if(n.getNext()!=null)
n.getNext().setLast(n.getLast());
else tail=n.getLast();
/**
* 处理尾节点
* 测试通过
*/
n.setNext(null);
n.setLast(null);
size--;
return n.getobject();
}
/**
* 交换两个索引的位置
* @param index_last
* @param index_next
*/
public void swap(int index_last,int index_next){
if((index_last<0||size<=index_last)||(index_next<0||size<=index_next)){
System.out.println("交换错误");
return ;
}
Object n1=this.getObject(index_last);
Object n2=this.getObject(index_next);
/**
* 方法1:直接用本身的删除和插入方法
* 如果到了最后一个用size判断
* 冒泡排序测试通过
* 方法2: 直接用代替方法,一步到位。
* 两种方法测试通过
*/
// this.remove(index_last);
// if(index_last==size) index_last-=1;
// this.insert(index_last, n2);
// this.remove(index_next);
// if(index_next==size) index_next-=1;
// this.insert(index_next, n1);
this.replace(index_last, n2);
this.replace(index_next, n1);
}
/**
* 批量删除
* 返回删除部分的长度
* @param index_start
* @param index_end
* @return
*/
public int removeBatch(int index_start,int index_end){
/*
* 满足end>start,和索引有效
*/
if((index_start>index_end)||(index_start<0||size<=index_start)||(index_end<0||size<=index_end)){
System.out.println("批量删除错误");
return 0;
}
/**
* 删除的start头的下一个指向end的下一个
* start->(end.next)
* (start.last)<-end
* start=end时测试通过
*/
Node n1=this.getNode(index_start);
Node n2=this.getNode(index_end);
n1.getLast().setNext(n2.getNext());
n2.getNext().setLast(n1.getLast());
/*
* 处理尾节点
*/
if(index_end==size-1){
tail=n1.getLast();
}
/**
* 删除的部分首尾指向空
*/
n1.setLast(null);
n2.setNext(null);
/*
* 返回删除的长度
*/
size=size-(index_end-index_start+1);
return 1+index_end-index_start;
}
/**
* 批量插入
* 返回插入后的链表大小
* @param index
* @param d
* @return
*/
public int insert(int index,DoubleLink d){
if(index<0||index>size){
System.out.println("插入错误");
return size;
}
/**
* 判断插入链表是否为0
* 测试通过
*/
if(d.getsize()==0){
return size;
}
Node n=this.getNode(index);
d.getNode(0).setLast(n.getLast());
d.getNode(d.getsize()-1).setNext(n);
n.getLast().setNext(d.getNode(0));
n.setLast(d.getNode(d.getsize()-1));
/**
* 处理尾节点
*/
if(index==size){
tail=d.getNode(d.getsize()-1);
}
size+=d.getsize();
return size;
}
/**
* 替换一个元素
* @param index
* @param e
* @return
*/
public Object replace(int index,Object e){
if(index<0||index>=size){
System.out.println("替换错误");
return null;
}
/**
* 直接用插入、删除操作
* 测试通过
*/
this.insert(index, e);
return this.remove(index+1);
}
/*
* 清空不解释
*/
public void clear(){
front.getNext().setLast(null);
front.setNext(null);
tail.getLast().setNext(null);
tail.setLast(null);
tail=null;
size=0;
}
}
- 学生类
package com.DoubleLinklist;
public class Student {
public int score;
protected char sex;
int age;
private String name;
/**
* 只用到学生的这两个方法
* @param score 分数
* @param sex 性别
* @param age 年龄
* @param name 姓名
*/
public Student(int score, char sex, int age, String name) {
this.score = score;
this.sex = sex;
this.age = age;
this.name = name;
}
public int getScore(){
return score;
}
public int getage(){
return age;
}
public String toString() {
return "姓名:" + name + "\t年龄:" + age + "\t性别:" + sex + "\t学分:" + score;
}
}
- 主函数
package com.DoubleLinklist;
import java.util.Random;
public class Manage {
public static void main(String [] args){
DoubleLink l=new DoubleLink();
DoubleLink newl=new DoubleLink();
Random rand=new Random();
/*
* 对链表L操作
* 加入元素
*/
int size=10;//rand.nextInt(20);
System.out.println("链表L要添加的元素个数为:"+size);
for(int i=0;i<size;i++){
Student stu=new Student(i*3%10,i%2==0? '男':'女',(i+10)%20,"name"+((char)('A'+i)));
l.add(stu);
}
/*
* 对链表newl操作
* 加入元素
*/
size=1;//rand.nextInt(7);
System.out.println("链表NEWL要添加的元素个数为:"+size);
for(int i=0;i<size;i++){
Student stu=new Student((i+1)*30,i%2==0? '男':'女',(i+10)%20,"name"+((char)('a'+i)));
newl.add(stu);
}
/**
* 输出两个链表
* l
* newl
*/
System.out.println("链表L中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
System.out.println("链表newl中存储的元素总数为:" + newl.getsize());
for (int i = 0; i < newl.getsize(); i++) {
Student stu = (Student ) newl.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
/**
* 对l链表操作:插入+替换测试
*/
System.out.println("在索引0处插入一个学生,同时在3处替换:");
Student s=new Student(99,'女',18,"mmm");
l.insert(0, s);
l.replace(3, s);
System.out.println(s.toString());
System.out.println("链表L中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
/**
* l链表测试删除操作
*/
System.out.println("删除学生:");
Student a=(Student) l.remove(1);
System.out.println(a.toString());
a=(Student)l.remove(9);
System.out.println(a.toString());
System.out.println("链表L中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
/*
* 插入新链表操作
*/
System.out.println("在链表L中索引3位置插入NEWL链表:");
l.insert(3, newl);
System.out.println("链表L中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
newl.remove(0);
System.out.println("newl队列删除第一个元素后"+newl.getsize());
/*
* 批量删除操作
*/
System.out.println("在链表L中批量删除索引4-6的元素:");
l.removeBatch(4, 6);
System.out.println("链表L中存储的元素总数为:" + l.getsize());
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
/**
* 对分数降序排序
*
*/
System.out.println("对分数降序排序:");
for(int i=0;i<l.getsize();i++){
for(int j=i;j<l.getsize();j++){
if(((Student)l.getObject(i)).getScore()<((Student)l.getObject(j)).getScore()){
l.swap(i, j);
}
}
}
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
/**
* 年龄降序
*/
System.out.println("对年龄降序排序:");
for(int i=0;i<l.getsize();i++){
for(int j=i;j<l.getsize();j++){
if(((Student)l.getObject(i)).getage()<((Student)l.getObject(j)).getage()){
l.swap(i, j);
}
}
}
for (int i = 0; i < l.getsize(); i++) {
Student stu = (Student ) l.getObject(i);
System.out.println("索引"+i+' '+stu.toString());
}
System.out.println("清空链表");
l.clear();
System.out.println("链表的元素为"+l.getsize());
}
}
- 测试样例结果
链表L要添加的元素个数为:10
链表NEWL要添加的元素个数为:1
链表L中存储的元素总数为:10
索引0 姓名:nameA 年龄:10 性别:男 学分:0
索引1 姓名:nameB 年龄:11 性别:女 学分:3
索引2 姓名:nameC 年龄:12 性别:男 学分:6
索引3 姓名:nameD 年龄:13 性别:女 学分:9
索引4 姓名:nameE 年龄:14 性别:男 学分:2
索引5 姓名:nameF 年龄:15 性别:女 学分:5
索引6 姓名:nameG 年龄:16 性别:男 学分:8
索引7 姓名:nameH 年龄:17 性别:女 学分:1
索引8 姓名:nameI 年龄:18 性别:男 学分:4
索引9 姓名:nameJ 年龄:19 性别:女 学分:7
链表newl中存储的元素总数为:1
索引0 姓名:namea 年龄:10 性别:男 学分:30
在索引0处插入一个学生,同时在3处替换:
姓名:mmm 年龄:18 性别:女 学分:99
链表L中存储的元素总数为:11
索引0 姓名:mmm 年龄:18 性别:女 学分:99
索引1 姓名:nameA 年龄:10 性别:男 学分:0
索引2 姓名:nameB 年龄:11 性别:女 学分:3
索引3 姓名:mmm 年龄:18 性别:女 学分:99
索引4 姓名:nameD 年龄:13 性别:女 学分:9
索引5 姓名:nameE 年龄:14 性别:男 学分:2
索引6 姓名:nameF 年龄:15 性别:女 学分:5
索引7 姓名:nameG 年龄:16 性别:男 学分:8
索引8 姓名:nameH 年龄:17 性别:女 学分:1
索引9 姓名:nameI 年龄:18 性别:男 学分:4
索引10 姓名:nameJ 年龄:19 性别:女 学分:7
删除学生:
姓名:nameA 年龄:10 性别:男 学分:0
姓名:nameJ 年龄:19 性别:女 学分:7
链表L中存储的元素总数为:9
索引0 姓名:mmm 年龄:18 性别:女 学分:99
索引1 姓名:nameB 年龄:11 性别:女 学分:3
索引2 姓名:mmm 年龄:18 性别:女 学分:99
索引3 姓名:nameD 年龄:13 性别:女 学分:9
索引4 姓名:nameE 年龄:14 性别:男 学分:2
索引5 姓名:nameF 年龄:15 性别:女 学分:5
索引6 姓名:nameG 年龄:16 性别:男 学分:8
索引7 姓名:nameH 年龄:17 性别:女 学分:1
索引8 姓名:nameI 年龄:18 性别:男 学分:4
在链表L中索引3位置插入NEWL链表:
链表L中存储的元素总数为:10
索引0 姓名:mmm 年龄:18 性别:女 学分:99
索引1 姓名:nameB 年龄:11 性别:女 学分:3
索引2 姓名:mmm 年龄:18 性别:女 学分:99
索引3 姓名:namea 年龄:10 性别:男 学分:30
索引4 姓名:nameD 年龄:13 性别:女 学分:9
索引5 姓名:nameE 年龄:14 性别:男 学分:2
索引6 姓名:nameF 年龄:15 性别:女 学分:5
索引7 姓名:nameG 年龄:16 性别:男 学分:8
索引8 姓名:nameH 年龄:17 性别:女 学分:1
索引9 姓名:nameI 年龄:18 性别:男 学分:4
newl队列删除第一个元素后0
在链表L中批量删除索引4-6的元素:
链表L中存储的元素总数为:7
索引0 姓名:mmm 年龄:18 性别:女 学分:99
索引1 姓名:nameB 年龄:11 性别:女 学分:3
索引2 姓名:mmm 年龄:18 性别:女 学分:99
索引3 姓名:namea 年龄:10 性别:男 学分:30
索引4 姓名:nameG 年龄:16 性别:男 学分:8
索引5 姓名:nameH 年龄:17 性别:女 学分:1
索引6 姓名:nameI 年龄:18 性别:男 学分:4
对分数降序排序:
索引0 姓名:mmm 年龄:18 性别:女 学分:99
索引1 姓名:mmm 年龄:18 性别:女 学分:99
索引2 姓名:namea 年龄:10 性别:男 学分:30
索引3 姓名:nameG 年龄:16 性别:男 学分:8
索引4 姓名:nameI 年龄:18 性别:男 学分:4
索引5 姓名:nameB 年龄:11 性别:女 学分:3
索引6 姓名:nameH 年龄:17 性别:女 学分:1
对年龄降序排序:
索引0 姓名:mmm 年龄:18 性别:女 学分:99
索引1 姓名:mmm 年龄:18 性别:女 学分:99
索引2 姓名:nameI 年龄:18 性别:男 学分:4
索引3 姓名:nameH 年龄:17 性别:女 学分:1
索引4 姓名:nameG 年龄:16 性别:男 学分:8
索引5 姓名:nameB 年龄:11 性别:女 学分:3
索引6 姓名:namea 年龄:10 性别:男 学分:30
清空链表
链表的元素为0