线性表常见的分为顺序表(vector和ArrayList)和链表(LinkedList)
单向链表只能由前驱找到后继,每个节点有数据和指向下一个节点的指针
优缺点:增删快,查找满
增加节点:新节点指针指向后一个节点,新节点的前一个节点指针指向新节点.
删除节点:被删除的节点的前一个节点指向被删除节点指向的后一个节点.
package com.cqc.LinkedList;
/**
* 线性表接口
* 和存储结构无关
* @author Administrator
*
*/
public interface List {
//返回线性表的大小,即数据元素的个数
public int size();
//返回线性表中序号为i的数据元素
public Object get(int i);
//判断线性表是否为空,空为true,否则为false
public boolean isEmpty();
//判断线性表是否包含数据元素node
public boolean contains(Node node);
//返回数据元素e在线性表中序号
public int indexOf(Node node);
//将数据元素e插入到线性表的i号位置
public void add(int i,Object e);
//将数据元素e插入到线性表末尾
public void add(Object e);
//删除线性表中序号为i的元素,并返回之
public Object remove(int i);
//删除线性表中第一个与node相同的元素
public boolean remove(Node node);
//替换线性表中序号为i的数据元素为e,返回原数据元素
public Object replace(int i,Object e);
}
/**
* 节点类,包含指针和数据
*/
public class Node {
public Object data; //数据
public Node next; //指向下一个节点的指针
//构造方法和重写toSting
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Node(Object data) {
this.data = data;
}
public Node() {
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
'}';
}
}
package com.cqc.LinkedList;
/**
* 查找,删除,增加核心方法是找到找到要操作节点的前一个节点,第0个的前一个节点为head节点,用head赋值即可得到第0个节点的前一个节点
*/
public class LinkedList implements List {
private Node head = new Node(); //头结点,不存储数据,为了使用方便,head为指向头结点的指针
private int size; //一共有几个节点
@Override
public int size() {
return size;
}
/**
* 需要从头结点开始查找
* @param i
* @return
*/
@Override
public Object get(int i) {
//找到i前一个节点,p初始值指向头结点
Node p = head;
//最终p指向第i-1个节点(从第0个开始,循环i次)
for(int j=0;j<i;j++){
p = p.next;
}
return p.next;
}
@Override
public boolean isEmpty() {
return size==0;
}
/**
* 查询是否包含此节点
* @param node
* @return
*/
@Override
public boolean contains(Node node) {
return indexOf(node)>=0;
}
@Override
public int indexOf(Node node) {
//p初始值指向头结点
Node p = head;
//最终p指向第size-1个节点(从第0个开始,循环size次)
for(int j=0;j<size;j++){
p = p.next;
if(p.next == node){ //p指向要查找的元素
return j;
}
}
return -1;
}
@Override
public void add(int i, Object e) {
//找到i前一个节点,p初始值指向头结点
Node p = head;
//最终p指向第i-1个节点(从第0个开始,循环i此)
for(int j=0;j<i;j++){
p = p.next;
}
//新创建一个节点,构造给了数据,指针未赋值
Node newNode = new Node(e);
//指明新节点的直接后继
//指针由前一个节点(i-1)得到
newNode.next= p.next;
//指明新节点的直接前驱,改变前一个节点,第(i-1)的指针域
p.next = newNode;
//添加完成后size++
size++;
}
@Override
public void add(Object e) {
this.add(size,e);
}
@Override
public Object remove(int i) {
//找到i前一个节点,p初始值指向头结点
Node p = head;
//最终p指向第i-1个节点(从第0个开始,循环i此)
for(int j=0;j<i;j++){
p = p.next;
}
//保存第i个节点对象
Object o = p.next;
//将第i个节点的指针域赋值给第i-1个节点的指针域
p.next = p.next.next;
//元素个数减一
size--;
return o;
}
/**
* 删除一个节点
* @param node
* @return
*/
public boolean remove(Node node) {
//找到i前一个节点,p初始值指向头结点
Node p = head;
Node beforeP; //beforeP指向p指向的节点的前一个节点
//最终p指向第size-1个节点(从第0个开始,循环size次)
for(int j=0;j<size;j++){
beforeP = p;
p = p.next;
if(p.next == node){ //p指向要删除的元素
beforeP.next = p.next;
//元素个数减一
size--;
return true;
}
}
return false;
}
@Override
public String toString() {
if(size == 0){
return "[]";
}
StringBuilder stringBuilder = new StringBuilder("["); //字符串开始符号为[
//定义一个Node,指向head
Node p = head;
for(int i = 0 ;i<size;i++){
p = p.next; //p指向第i个节点
if(i !=size-1){
stringBuilder.append(p.data+",");
}else {
stringBuilder.append(p.data);
}
}
stringBuilder.append("]"); //字符串结尾符号
return stringBuilder.toString();
}
}
增加一个节点newNode(数据和指针域),需要知道该节点之前的节点,则获取该节点之前的节点是核心内容第一个节点的前一个节点是头结点,则获取指向头结点的指针是前提
① 由无参构造器new出LinkedList时,同时new出了一个节点head和size变量
② head是指向头结点的指针
③流程图
②示意图
使用p指针指向头结点,根据参数i可以获取到要添加节点的前一个节点,通过指向新节点指针和指向第2个节点指针的引用和替换,借壳完成增加操作.
public Object get(int i) {
//找到i前一个节点,p初始值指向头结点
Node p = head;
//最终p指向第i-1个节点(从第0个开始,循环i次)
for(int j=0;j<i;j++){
p = p.next;
}
return p.next;
}
核心方法还是找到头结点,由p指针依次按照i的值循环查找到i节点的前一个节点