实现双向链表
一、该链表类的各方法解释(最后在附上完整代码)
该链表有一个头指针head指向第一个结点,一个尾指针指向最后一个结点
1)结点类
/**
* 结点类
*/
private static class Node<T>{
Node<T> next; //保存前继结点
Node<T> prev; //保存后继节点
T value; //该结点的数据
/**
* @param prev 上一个节点
* @param value 结点保存的数据
* @param next 下一个结点
*/
public Node(Node<T> prev , T value , Node<T> next) {
this.prev = prev; //前继结点
this.value = value; //数据
this.next = next; //后继结点
}
}
2)插入结点在第一个位置上
/**
* 插入结点第一个位置上
*/
public void addFirst(T value) {
linkFirst(value);
}
/**
* 插入结点在第一个位置上
*/
private void linkFirst(T value) {
Node<T> h = head; //因为头指针是指向了第一个结点,所以h保存的是第一个结点
Node<T> newNode = new Node<T>(null , value , h); //新结点的next指向第一个结点h,第一个结点的前继指针为null
head = newNode; //头结点指向新的结点
//如果头指针为空,说明新的结点即是第一个结点, 也是最后一个结点
if(h == null) {
last = newNode; //让尾指针指向最后一个结点
}else {
//如果不为空, 将第h的前继指针pre指向新的结点
h.prev = newNode;
}
size++; //长度加1
}
3) 添加结点
/**
* 添加结点
*/
public void add(T value) {
linkLast(value);
}
/**
* 添加最后一个结点
*/
private void linkLast(T value) {
Node<T> l = last; //l保存最后一个结点
Node<T> newNode = new Node<T>(l , value , null); //新的结点的前继指针pre指向最后一个结点l
last = newNode; //尾指针指向新的结点
if(l == null) { //判断如果原来最后的结点是否为空, 如果为空, 则是空链表, 所以新的结点是链表中的第一个结点, 就直接让头指针head指向新的结点
head = newNode;
}else {
l.next = newNode; //原来的最后结点的next指针指向新的结点
}
size++; //长度加1
}
4)删除第一个结点
/**
* 删除第一个结点
*/
public T removeFirst() {
return unLinkFirst();
}
/**
* 删除第一个结点
*/
private T unLinkFirst() {
checkedIsNullException(); //检查是否为空链表,如果是空链表,则抛出异常
Node<T> h = head; //保存第一个结点
head = head.next; //头指针指向第二个结点,head.next是第二个结点
if(head == null) { //如果第二个结点为空,说明链表只有一个结点,删除第一个结点后,链表为空,所以为指针也要改变
last = null;
}
size--; //长度减1
return h.value; //返回删除结点的值
}
5) 删除最后一个结点
/**
* 删除尾结点
*/
public T removeLast() {
return unLinkLast(true);
}
/**
* 删除最后一个元素
*/
private T unLinkLast(boolean isRemove) {
checkedIsNullException(); //检查是否为空链表,如果是空链表,则抛出异常
Node<T> l = last; //l保存最后一个结点
T val = l.value; //获取最后一个结点的数据
if(isRemove) {
last = l.prev; //把尾指针指向倒数第二个结点
l = null; //因为l保存的是最后一个结点,所以把l设置为null
if(last == null) //如果last等于空,说明删除之前链表中只有一个结点(也是第一个结点,第一个结点的前继指针是为null的),所以删除后链表没有结点了,把头指针指向null
head = null;
}
size--; //长度减1
return val; //返回删除的结点的数据
}
6)查找结点
/**
* 获取第i个结点的元素
*/
public T get(int i) {
return node(i).value;
}
/**
* 设置向前查找和向查找,是为了增加效率,如果链表有100个结点,如果我要查第99个结点,如果从第一个结点开始找,
* 那样效率会很慢.如果要不出现这种情况呢,那么就要从最后一个结点找起
* 100/2 = 50
* 如果查找的元素的大于50的,那么就从后往前找,如果小于50的,就从第一个结点开始找
*
*/
private Node<T> node(int index){
checkedPosition(index); //检查index时候超出链表的范围,如果是, 则抛出异常
if(index < (size/2)) { //如果index小于链表的(长度/2),从第一个结点向前查找
Node<T> thead = head; //thead保存第一个结点
/**
* 遍历链表查找结点
*/
for(int i = 0; i < index; i++)
thead = thead.next; //继续遍历下一个结点
return thead; //返回找到的结点
}else {
Node<T> thead = last; //thead保存最后一个结点
/**
* 遍历链表查找结点
*/
for(int i = size-1; i > index; i--)
thead = thead.prev; //继续遍历上一个结点
return thead; //返回找到的结点
}
}
7)链表是否为空
/**
* 判断链表是否为空
*/
public boolean isEmpty() {
return head == null; //如果头指针是指向空
}
8)链表的长度
/**
* 返回链表的长度
*/
public int size() {
return size;
}
9)找指定值出现的第一次的位置
/**
* 返回value所出现的第一个位置
*/
public int indexOf(Object value) {
return getIndexOfPosition(value);
}
/**
* 返回value所出现的第一个位置
*/
private int getIndexOfPosition(Object value) {
int index = 0; //结点所在的位置
for(Node<T> x = head; x != null; x = x.next) { //首先从第一个结点开始找,条件是x不等于0,如果等于0,说明遍历完了
if(x.value.equals(value)) { //如果遍历到的结点的数据等于value,说明找到了,返回index
return index;
}
index++;
}
return -1; //找不到,返回-1
}
10)返回指定值出现的最后一次的位置
/**
* 返回value所最后出现的位置
*/
public int lastIndexOf(Object value) {
return getIndexLastPosition(value);
}
/**
* 返回value出现的最后一个位置
*/
private int getIndexLastPosition(Object value) {
int index = size; //链表的长度
for(Node<T> x = last; x != null; x = x.prev) {
index--;
if(x.value.equals(value)) {
return index;
}
}
return -1;
}
11)是否存在该数据
/**
* 判断链表中是否包含value元素
*/
public boolean contain(Object value) {
return indexOf(value) > -1; //如果找到的位置大于-1,说明链表有该数据的结点
}
12)打印链表
public String toString() {
String str = "";
for(int i = 0; i < size(); i++) {
str = str + this.get(i)+" ";
}
return str;
}
13)检查是否为空链表
private void checkedIsNullException() {
if(isEmpty())
throw new NullPointerException("空链表......");
}
14)检查是否超出链表的范围
private void checkedPosition(int index) {
if(index < 0 || index >= size)
throw new ArrayIndexOutOfBoundsException("index="+index+", size="+size);
}
完整代码
package cn.zyz.b_zy; /** * 双向链表 */ public class AA<T> { private Node<T> head , last; //head为头指针, 指向第一个结点; last指向最后一个结点 private int size = 0; //链表的长度 /** * 结点类 */ private static class Node<T>{ Node<T> next; //保存下一个结点 Node<T> prev; //保存上一个节点 T value; //该结点的数据 /** * @param prev 上一个节点 * @param value 结点保存的值 * @param next 下一个结点 */ public Node(Node<T> prev , T value , Node<T> next) { this.prev = prev; //前继结点 this.value = value; //数据 this.next = next; //后继结点 } } /** * 添加结点 */ public void add(T value) { linkLast(value); } /** * 添加最后一个结点 */ private void linkLast(T value) { Node<T> l = last; //l保存最后一个结点 Node<T> newNode = new Node<T>(l , value , null); //新的结点的前继指针pre指向最后一个结点l last = newNode; //尾指针指向新的结点 if(l == null) { //判断如果原来最后的结点是否为空, 如果为空, 则是空链表, 所以新的结点是链表中的第一个结点, 就直接让头指针head指向新的结点 head = newNode; }else { l.next = newNode; //原来的最后结点的next指针指向新的结点 } size++; //长度加1 } /** * 插入结点第一个位置上 */ public void addFirst(T value) { linkFirst(value); } /** * 插入结点在第一个位置上 */ private void linkFirst(T value) { Node<T> h = head; //因为头指针是指向了第一个结点,所以h保存的是第一个结点 Node<T> newNode = new Node<T>(null , value , h); //新结点的next指向第一个结点h,第一个结点的前继指针为null head = newNode; //头结点指向新的结点 //如果头指针为空,说明新的结点即是第一个结点, 也是最后一个结点 if(h == null) { last = newNode; //让尾指针指向最后一个结点 }else { //如果不为空, 将第h的前继指针pre指向新的结点 h.prev = newNode; } size++; //长度加1 } /** * 删除第一个结点 */ public T removeFirst() { return unLinkFirst(); } /** * 删除第一个结点 */ private T unLinkFirst() { checkedIsNullException(); //检查是否为空链表,如果是空链表,则抛出异常 Node<T> h = head; //保存第一个结点 head = head.next; //头指针指向第二个结点,head.next是第二个结点 if(head == null) { //如果第二个结点为空,说明链表只有一个结点,删除第一个结点后,链表为空,所以为指针也要改变 last = null; } size--; //长度减1 return h.value; //返回删除的结点的数据 } /** * 删除尾结点 */ public T removeLast() { return unLinkLast(); } /** * 删除最后一个元素 */ private T unLinkLast() { checkedIsNullException(); //检查是否为空链表,如果是空链表,则抛出异常 Node<T> l = last; //l保存最后一个结点 T val = l.value; //获取最后一个结点的数据 last = l.prev; //把尾指针指向倒数第二个结点 l = null; //因为l保存的是最后一个结点,所以把l设置为null if(last == null){ //如果last等于空,说明删除之前链表中只有一个结点(也是第一个结点,第一个结点的前继指针是为null的),所以删除后链表没有结点了,把头指针指向null head = null; } size--; //长度减1 return val; //返回删除的结点的数据 } /** * 获取第i个结点的元素 */ public T get(int i) { return node(i).value; } /** * 设置向前查找和向查找,是为了增加效率,如果链表有100个结点,如果我要查第99个结点,如果从第一个结点开始找, * 那样效率会很慢.如果要不出现这种情况呢,那么就要从最后一个结点找起 * 100/2 = 50 * 如果查找的元素的大于50的,那么就从后往前找,如果小于50的,就从第一个结点开始找 * */ private Node<T> node(int index){ checkedPosition(index); //检查index时候超出链表的范围,如果是, 则抛出异常 if(index < (size/2)) { //如果index小于链表的(长度/2),从第一个结点向前查找 Node<T> thead = head; //thead保存第一个结点 /** * 遍历链表查找结点 */ for(int i = 0; i < index; i++) thead = thead.next; //继续遍历下一个结点 return thead; //返回找到的结点 }else { Node<T> thead = last; //thead保存最后一个结点 /** * 遍历链表查找结点 */ for(int i = size-1; i > index; i--) thead = thead.prev; //继续遍历上一个结点 return thead; //返回找到的结点 } } /** * 返回value所最后出现的位置 */ public int lastIndexOf(Object value) { return getIndexLastPosition(value); } /** * 返回value出现的最后一个位置 */ private int getIndexLastPosition(Object value) { int index = size; for(Node<T> x = last; x != null; x = x.prev) { index--; if(x.value.equals(value)) { return index; } } return -1; } /** * 返回value所出现的第一个位置 */ public int indexOf(Object value) { return getIndexOfPosition(value); } /** * 返回value所出现的第一个位置 */ private int getIndexOfPosition(Object value) { int index = 0; //结点所在的位置 for(Node<T> x = head; x != null; x = x.next) { //首先从第一个结点开始找,条件是x不等于0,如果等于0,说明遍历完了 if(x.value.equals(value)) { //如果遍历到的结点的数据等于value,说明找到了,返回index return index; } index++; } return -1; //找不到,返回-1 } /** * 判断链表中是否包含value元素 */ public boolean contain(Object value) { return indexOf(value) > -1; } /** * 返回链表的长度 */ public int size() { return size; } public String toString() { String str = ""; for(int i = 0; i < size(); i++) { str = str + this.get(i)+" "; } return str; } private void checkedIsNullException() { if(isEmpty()) throw new NullPointerException("空链表......"); } private void checkedPosition(int index) { if(index < 0 || index >= size) throw new ArrayIndexOutOfBoundsException("index="+index+", size="+size); } /** * 判断链表是否为空 */ public boolean isEmpty() { return head == null; //如果头指针是指向空 } }