链式线性表
线性表的相关概念:
链式线性表的定义:用一组任意的存储单元存储线性表的数据元素,这里的存储单元可以是连续也可以是不连续的。
特点:
- 存在唯一的可以被称为“第一个”的数据元素
- 存在唯一的可以被称为“最后一个”的数据元素
- 除了第一个元素之外,每个元素都只有一个唯一的前驱
- 除了最后一个元素之外,每个元素都只有一个唯一的后继
线性表可以用数组和链表实现,笔记中主要记录如何用用链表区实现线性表的。
线性链表:是用链表实现的线性表,链表中的每一个存储单元称之为结点。每一个结点中只有一个指针域的结点的称之为单链表;在单链表中链表的尾部指向头部的称之为循环链表;每个结点中有两个指针域的称之为双链表。
结点:由数据域和指针域组成,数据域用于存放数据,指针域用于指向(前驱)后继的元素,即(上)下一个结点。
1、单向链表的实现
1.1、单链表的节点实现:
- 定义一个数据域用于存放数据,在这里使用到了泛型
- 定义一个指针域用于存放后继的结点位置
备注:这里可以使用 data 和 next 这两个成员属性定义为私有,即使用 private 修饰,提供相应的 get 和 set 方法。
class SingleLinkNode<T> {
// 数据域:用于存放数据
public T data;
// 指针域:用于指向后继的元素,即下一个结点
public SingleLinkNode next;
// 构造方法
pulbic SingleLinkNode(){
super();
}
public SingleLinkNode(T t){
this.data = t ;
this.next= null;
}
}
1.2、单链表的实现
本次实现中已将 SingleLinkNode 作为SingleLink内部类,所以无需再提供额外的 SingleLinkNode 类。
public class SingleLink<T>{
// 创建一个头指针
private SingleLinkNode head;
public SingleLink(){
head = new SingleLinkNode();
this.head.data = null;
this.head.next = null;
}
// 获取线性表的长度
public int size(){
SingleLinkNode temp = head;
int size = 0 ;
while(temp.next != null){
temp = temp.next;
size++;
}
return size;
}
// 清空线性表
public void clear(){
// 将链表的头指针置空即可
this.head.next = null;
}
// 获取指定链表中的数据
public Object get(int index){
// 判断获取的位置是否合理
int size = this.size();
if(index < 0 || index >= size){
throw new RuntimeException("获取的位置不合理!");
}
//创建临时变量
//将index定位到指定元素第一次出现的位置
SingleLinkNode temp = locateNode(index);
//返回节点中的数据
return temp.data;
}
// 定位到data第一次出现的位置
public int locate(T data){
int index = -1;
int tempIndex = 0;
SingleLinkNode temp = head.next;
while(temp != null){
if(data.equals(temp.data)){
index = tempIndex;
break;
}
temp = temp.next;
tempIndex++;
}
// 如果返回的是-1,则代表没有找到
return index;
}
//返回到data第一次出现的节点
private SingleLinkNode locateNode(int index){
SingleLinkNode temp = head;
while(index >= 0){
temp = temp.next;
index--;
}
return temp;
}
// 添加数据,没有指定的位置的话,默认在队列的尾部插入
public void add(T data){
// 判断加入的数据是否为空
if(data == null){
throw new RuntimeException("插入的数据不能为空");
}
SingleLinkNode insertNode = new SingleLinkNode(data) ;
SingleLinkNode temp = this.head;
while(temp.next != null){
temp = temp.next;
}
insertNode.next =null;
temp.next = insertNode;
}
// 重载add方法,在index位置上插入的数据,index和数组索引一样从 0 开始
public void add(int index,T data){
// 判断插入的位置是否合理
if(index <0 || index >this.size()){
throw new RuntimeException("插入的位置不合理");
}
//判断插入的数据是否为空,
if(data == null){
throw new RuntimeException("插入的数据不能为空");
}
// 创建一个插入的节点
SingleLinkNode insertNode = new SingleLinkNode(data);
SingleLinkNode temp = locateNode(index -1 );
insertNode.next = temp.next;
temp.next = insertNode;
}
//删除指定索引的元素
public Object remove(int index){
// 判断删除的位置是否合理
if(index <0 || index >= this.size()){
throw new RuntimeException("插入的位置不合理");
}
SingleLinkNode temp = locateNode(index -1 );
SingleLinkNode delNode = temp.next;
temp.next = delNode.next;
return delNode.data;
}
// 删除链表中第一次出现的data数据返回,删除的data的索引
public int remove(T data){
//
int index = -1;
int tempIndex = 0;
SingleLinkNode temp = head.next;
// 这里要考虑在遍历的时候能否也将第一个和最后一个也进行数据的检查
while(temp != null){
//检查下一位的data是否与传入的数据相等
if(data.equals(temp.next.data)){
// 有可能data数据刚好在最后一个,如果直接进行 temp.next = temp.next.next的,可能会出现空指针异常
if(temp.next != null){
temp.next = temp.next.next;
}
index = tempIndex;
break; // 跳出循环
}
temp = temp.next;
tempIndex++;
}
// 如果返回的是-1,则代表没有找到
return index;
}
public void showLink(){
SingleLinkNode temp = this.head;
System.out.print("[");
while(temp.next != null ){
temp = temp.next;
System.out.print(temp.data);
if(temp.next != null){
System.out.print(",");
}
}
System.out.println("]");
}
public boolean isEmpty(){
return this.head.next == null;
}
// 使用private 修饰符,将SingleLinkNode封装进入SingleLink中,不直接暴露外部
private class SingleLinkNode<T> {
public T data;
public SingleLinkNode next;
public SingleLinkNode(){
super();
}
public SingleLinkNode(T t){
this.data = t ;
this.next= null;
}
@Override
public String toString() {
return "SingleLinkNode{" +
"data=" + data +
'}';
}
}
}
2、双向链表
2.1、双链表结点的实现
class DoubleLinkNode<T>{
public T data;
// 前驱指针
public DoubleLinkNode pre;
// 后继指针
public DoubleLinkNode next;
//构造方法
public DoubleLinkNode(){}
public DoubleLinkNode(T data){
this.data = data;
}
}
2.2、双链表的实现
public class DoubleLink<T>{
private DoubleLinkNode head;
public DoubleLink(){
this.head = new DoubleLinkNode();
this.head.data = null;
this.head.pre = null;
this.head.next = null;
}
// 获取链表的长度
public int size(){
DoubleLinkNode temp = head.next;
int size = 0 ;
while(temp != null){
temp = temp.next;
size++;
}
return size;
}
// 清空线性表
public void clear(){
// 将链表的头指针置空即可
this.head.next = null;
}
// 获取指定链表中的数据
public Object get(int index){
// 判断获取的位置是否合理
int size = this.size();
if(index < 0 || index >= size){
throw new RuntimeException("获取的位置不合理!");
}
//创建临时变量
//将index定位到指定元素第一次出现的位置
DoubleLinkNode temp = locateNode(index);
//返回节点中的数据
return temp.data;
}
// 定位到data第一次出现的位置
public int locate(T data){
int index = -1;
int tempIndex = 0;
DoubleLinkNode temp = head.next;
while(temp != null){
if(data.equals(temp.data)){
index = tempIndex;
break;
}
temp = temp.next;
tempIndex++;
}
// 如果返回的是-1,则代表没有找到
return index;
}
//返回到data第一次出现的节点
private DoubleLinkNode locateNode(int index){
// 判断获取的位置是否合理
int size = this.size();
if(index < 0 || index >= size){
throw new RuntimeException("获取的位置不合理!");
}
DoubleLinkNode temp = head;
while(index >= 0){
temp = temp.next;
index--;
}
return temp;
}
// 添加数据,没有指定的位置的话,默认在队列的尾部插入
public void add(T data){
// 判断加入的数据是否为空
if(data == null){
throw new RuntimeException("插入的数据不能为空");
}
DoubleLinkNode insertNode = new DoubleLinkNode(data) ;
DoubleLinkNode temp = this.head;
while(temp.next != null){
temp = temp.next;
}
temp.next = insertNode;
insertNode.pre = temp;
insertNode.next =null;
}
// 重载add方法,在index位置上插入的数据,index和数组索引一样从 0 开始
public void add(int index,T data){
// 判断插入的位置是否合理
if(index <0 || index > this.size()){
throw new RuntimeException("插入的位置不合理");
}
//判断插入的数据是否为空,
if(data == null){
throw new RuntimeException("插入的数据不能为空");
}
if (index == size()){
this.add(data);
return;
}
// 创建一个插入的节点
DoubleLinkNode insertNode = new DoubleLinkNode(data);
DoubleLinkNode temp = locateNode(index);
insertNode.pre = temp.pre;
temp.pre.next = insertNode;
insertNode.next = temp;
temp.pre = insertNode;
}
//删除指定索引的元素
public Object remove(int index){
// 判断删除的位置是否合理
if(index <0 || index >= this.size()){
throw new RuntimeException("删除的位置不合理");
}
DoubleLinkNode delNode = locateNode(index);
delNode.pre.next = delNode.next;
// 如果del是最后一个元素,会出现空指针异常,进行判断避免空指针异常
if( delNode.next != null ){
delNode.next.pre = delNode.pre;
}
return delNode.data;
}
// 删除链表中第一次出现的data数据返回,删除的data的索引
public int remove(T data) {
//
int index = -1;
int tempIndex = 0;
DoubleLinkNode temp = head.next;
// 这里要考虑在遍历的时候能否也将第一个和最后一个也进行数据的检查
while (temp != null) {
//检查下一位的data是否与传入的数据相等
if (data.equals(temp.data)) {
temp.pre.next = temp.next;
// 有可能data数据刚好在最后一个,如果直接进行 temp.next = temp.next.next的,可能会出现空指针异常
if (temp.next != null) {
temp.next.pre = temp.pre;
}
index = tempIndex;
break; // 跳出循环
}
temp = temp.next;
tempIndex++;
}
// 如果返回的是-1,则代表没有找到
return index;
}
public void showLink () {
DoubleLinkNode temp = this.head;
System.out.print(" [");
while (temp.next != null) {
temp = temp.next;
System.out.print(temp.data);
if (temp.next != null) {
System.out.print(",");
}
}
System.out.println("]");
}
public boolean isEmpty() {
return this.head.next == null;
}
private class DoubleLinkNode<T>{
public T data;
// 前驱指针
public DoubleLinkNode pre;
// 后继指针
public DoubleLinkNode next;
// 构造方法
public DoubleLinkNode() {
}
// 构造方法
public DoubleLinkNode(T data) {
this.data = data;
}
}
}
3、循环链表的实现
3.1、循环链表节点的实现
循环链表的结点实现是单链表是一样
class CircleLinkNode<T>{
public T data;
public CircleLinkNode next;
//提供构造方法
public CircleLinkNode(){}
public CircleLinkNode(T data){
this.data = data;
}
}
3.2 循环链表的实现
public class CircleLink{
private CircleLinkNode head;
public CircleLink(){
this.head = new CircleLinkNode() ;
haed.data = null;
//在循环列表中,列表为空的时候,头指针指向自己
head.next =head;
}
public int size(){
int size = 0;
if(head.date == null && head.next == head){
return size;
}
size = 1 ;
CircleLinkNode temp= head;
while(temp.next != head){
size++;
temp = temp.next;
}
return size;
}
// 判断是否为空
public boolean isEmpty(){
if (head.data = null && head.next == head){
return true;
}
return false;
}
// 定位到指定元素的缩影
private CircleLinkNode locateNode(int index){
if(isEmpty()){
throw new RuntimeException("链表为空");
}
if(index < 0 || index >= size()){
throw new RuntimeException("查找的位置不合理");
}
CircleLinkNode temp = head;
while(index > 0){
temp =temp.next;
index--;
}
return temp;
}
// 返回index位置的数据域
public Object locate(index){
locateNode(index).data;
}
//是在链表的尾部添加
public void add(T dada){
if(data == null){
throw new RuntimeException("插入的数据不能为空");
}
if(head.data = null){
head.data =data;
}else{
CircleLinkNode insertNode = new CircleLinkNode(data);
CircleLinkNode temp= head;
while(temp.next != head){
temp = temp.next;
}
insertNode.next =temp.next;
temp.next = insertNode;
}
}
//在指定的位置插入
public void add(int index,T data){
//检查插入的数据
if(data == null){
throw new RuntimeException("插入的数据不能为空");
}
// 检查插入的位置是否合理
if(index < 0 || index > size()){
throw new RuntimeException("插入的位置不合理");
}
// 经过前面的检查就可以进行操作操作了
CircleLinkNode insertNode = new CircleLinkNode(data);
CircleLinkNode temp= head;
// 判断是否插入到链表的头部
if(index == 0 ){
//定位到链表的尾部
while(temp.next != head){
temp = temp.next;
}
//直接插入到链表的尾部
insertNode.next =temp.next;
temp.next = insertNode;
//最后直接将insertNode设置为head
head = insertNode;
return ;
}
// 定位到插入位置的前一个元素
CircleLinkNode temp = locateNode(index-1);
insertNode.next = temp.next;
temp.next = insertNode;
}
// 删除链表中指定位置的元素
public Object remove(int index){
if(index < 0 || index >= size()){
throw new RuntimeException("删除的位置不合理")
}
if(index == 0){
data = head.data;
CircleLinkNode temp = head;
while(temp.next == head){
temp = temp.next;
}
temp.next = head.next;
head = head.next;
return data;
}
//定位到index前一位,即index-1
CircleLinkNode preNode = locateNode(index - 1 );
Object data = preNode.next.data;
preNode.next =preNode.next.next;
return data;
}
}
}