前言
实现参考:
http://blog.csdn.net/hitwhylz/article/details/12305021
http://www.cnblogs.com/smyhvae/p/4761593.html
建议阅读以上博客,我的博客更偏向于[我]能看懂
上面的博客则偏向于[大家]都能看懂
实现的功能
增加
- 指定位置插入节点
- 在末尾增加节点
删除
- 删除指定节点,通过节点数据(假设数据不重复)
- 删除指定位置节点
修改
- 修改指定位置节点数据
查询
- 查询某个节点的位置
- 查询某个位置中的节点数据
- 查询某个数据是否存在于链表中(假设数据不重复)
转数组
- 将链表转成数组
中文版伪代码
/**
*
* 单向链表
* 节点:
* 数据都存放在一个节点中
* 每个节点都有指向下一个节点的指针
* 头指针:
* 链表中的头个节点,不计入节点数,表示链表头
* 当前指针:
* 在链表中游走,指向哪,跑到哪。
* 增加
* 新增节点到尾部时
* 空链表:新增的节点成为头指针的next指向的节点
* 非空链表:成为最后一个节点的下一个节点,更新当前指针为下一个节点
* 新增节点到第n个位置时
* 如果插入的位置不是头:
* 到达第n-1个节点
* 新节点指向第n-1个的next节点
* 第n-1个节点的next指向新节点
* 如果是第一个位置(头):
* 新节点的next指向头指针的节点
* 更新头指针的next指向新节点
* 删除
* 根据数据删除
* 判断数据是否存在或者是否为空
* 如果存在,且位置为n
* 将当前指针指向第n-1个节点
* 更新第n-1个节点的next指向第n+1个节点
* 否则
* 抛出异常
* 根据传入的位置n删除
* 判断参数n是否合法 (n >= 0 && n < size)
* 若合法
* 将当前指针指向第n-1个节点
* 更新第n-1个节点的next指向第n+1个节点
* 否则
* 抛出异常
*
* 查询
* 根据数据查询节点位置
* 判断输入数据的合法性
* 合法
* 遍历查找
* 找到,返回位置
* 没找到,返回-1
* 获取准确数据,根据传入位置n查询
* 判断输入数据的合法性
* 定位到n,返回数据
* 存在性查询,根据传入数据查询链表中是否存在这样一个节点
* 判断输入数据的合法性
* 遍历查询
* 存在,返回真
* 不存在,返回假
* 修改
* 传入数据和位置
* 验证位置的合法性
* 合法
* 当前指针跳转到目标节点修改
* 返回真
* 不合法
* 返回假
*/
具体实现
public class SingleLinkedList<T> {
private Node headNode;
private Node currNode;
private int size;
SingleLinkedList() {
headNode = new Node();
currNode = null;
size = 0;
}
public int Size() {
return size;
}
//添加节点到链表尾部
public void insert(T data) {
Node tNode = new Node(data);
if (isEmpty()) {
headNode.setNext(tNode);
} else {
toIndexOf(size - 1);
currNode.setNext(tNode);
}
size++;
}
//添加节点到第n个位置
public void insert(T data, int n) {
//将当前指针定位到第n-1个节点
toIndexOf(n - 1);
//如果为空,插入到第一个节点
if (isEmpty()) {
insert(data);
return;
}
// 将新节点的next指向第n个节点, 第n-1个节点的next指向新节点
// 即新节点为第n个,原第n个节点为n+1
currNode.setNext(new Node(data, currNode.getNext()));
size++;
}
//查询第n个节点的数据
public <T> T get(int n) {
//当前指针移动到第n个节点
toIndexOf(n);
return (T) currNode.getData();
}
//查询某节点的位置
public int getElemAt(T data) {
if (data == null) {
throw new NullPointerException("既然喜欢null,那就给你咯。");
}
currNode = headNode.getNext();
int i = 0;
while (currNode != null) {
if (currNode.getData().equals(data)) {
return i;
}
i++;
currNode = currNode.getNext();
}
return -1;
}
//修改节点的数据
public boolean setElemAt(T data, int n) {
toIndexOf(n);
currNode.setData(data);
return true;
}
//查询某节点是否存在
public boolean contains(T data) {
return getElemAt(data) == -1 ? false : true;
}
//删除第n个节点
public T delete(int n) {
toIndexOf(n - 1);
T data = currNode.getNext().getData();
currNode.setNext(currNode.getNext().getNext());
size--;
return data;
}
//删除指定节点
public T deleteElemBy(T data) {
int n = getElemAt(data);
return delete(n);
}
public boolean isEmpty() {
return size == 0 ? true : false;
}
//将其转为数组
public Object[] toArray() {
if (isEmpty()) {
throw new IndexOutOfBoundsException("Size: " + size);
}
Object[] arr = new Object[size];
toIndexOf(0);
int i = 0;
while (currNode != null) {
arr[i++] = currNode.getData();
currNode = currNode.getNext();
}
return arr;
}
//将当前指针移动到指定位置
private void toIndexOf(int n) {
if (isEmpty() || !isIndexOk(n)) {
throw new IndexOutOfBoundsException("Index: " + n + ", Size: " + size);
}
currNode = headNode;
if (n == -1) return;
while (n != -1) {
currNode = currNode.getNext();
n--;
}
}
private boolean isIndexOk(int n) {
if (n < -1 || n >= size) return false;
return true;
}
private class Node {
private T data;
private Node next;
Node() {
}
Node(T data) {
this.data = data;
}
Node(T data, Node nextNode) {
this.data = data;
this.next = nextNode;
}
public T getData() {
return data;
}
public Node getNext() {
return next;
}
public void setData(T data) {
this.data = data;
}
public void setNext(Node next) {
this.next = next;
}
}
}
测试
public class LinkedListTest {
public static void main(String[] args) {
SingleLinkedList<Integer> test = new SingleLinkedList<Integer>();
for (int i = 0; i < 10; i++) {
test.insert(i);
}
//System.out.println("------------越界测试--------------");
//System.out.println(test.get(-1));
//System.out.println(test.get(test.Size()));
System.out.println("角标读取:");
System.out.print(test.get(0) + ",");
System.out.print(test.get(4) + ",");
System.out.println(test.get(test.Size() - 1));
System.out.println("------------转换数组--------------");
Object[] data = test.toArray();
showArray(data);
System.out.println("------------根据元素获取角标--------------");
int el1 = 2, el2 = 22;
System.out.println(el1 + " at " + test.getElemAt(2));
System.out.println(el2 + " at " + test.getElemAt(22));
System.out.println("------------原数组--------------");
data = test.toArray();
showArray(data);
System.out.println("角标删除测试,删除");
System.out.print(test.delete(9) + ",");
System.out.print(test.delete(0) + ",");
System.out.println(test.delete(3));
System.out.println("删除完毕");
data = test.toArray();
showArray(data);
System.out.println("元素删除测试,删除:");
System.out.print(test.deleteElemBy(1) + ",");
System.out.print(test.deleteElemBy(8) + ",");
System.out.println(test.deleteElemBy(5));
System.out.println("删除完毕:");
data = test.toArray();
showArray(data);
System.out.println("------------包含测试--------------");
data = test.toArray();
showArray(data);
int n1 = 6, n2 = 10;
System.out.println(n1 + ",在吗?" + test.contains(n1));
System.out.println(n2 + ",在吗?" + test.contains(n2));
System.out.println("------------插入测试--------------");
data = test.toArray();
showArray(data);
test.insert(4, 2);
test.insert(5, 3);
System.out.println("插入4、5分别到第2、3个位置,完成:");
data = test.toArray();
showArray(data);
System.out.println("------------修改测试--------------");
data = test.toArray();
showArray(data);
test.setElemAt(1024, 2);
test.insert(2048, 3);
System.out.println("修改角标2、3分别到为1024、2048,完成:");
data = test.toArray();
showArray(data);
test.setElemAt(6, 10);
}
public static void showArray(Object[] data) {
int len = data.length - 1;
for (int i = 0; i <= len; i++) {
System.out.print(data[i]);
System.out.print(i < len ? " " : "");
}
System.out.println();
}
}
结果
角标读取:
0,4,9
------------转换数组--------------
0 1 2 3 4 5 6 7 8 9
------------根据元素获取角标--------------
2 at 2
22 at -1
------------原数组--------------
0 1 2 3 4 5 6 7 8 9
角标删除测试,删除
9,0,4
删除完毕
1 2 3 5 6 7 8
元素删除测试,删除:
1,8,5
删除完毕:
2 3 6 7
------------包含测试--------------
2 3 6 7
6,在吗?true
10,在吗?false
------------插入测试--------------
2 3 6 7
插入4、5分别到第2、3个位置,完成:
2 3 4 5 6 7
------------修改测试--------------
2 3 4 5 6 7
修改角标2、3分别到为1024、2048,完成:
2 3 1024 2048 5 6 7
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 10, Size: 7
at SingleLinkedList.toIndexOf(SingleLinkedList.java:183)
at SingleLinkedList.setElemAt(SingleLinkedList.java:135)
at LinkedListTest.main(LinkedListTest.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
收获
第一次用泛型(大概是乱用)
静态内部类和非静态内部类的区别
静态内部类不能访问外部类的非静态成员
类似于把静态内部类独立了
意识到链表的各种操作十分耗时!!查询一个,查一个就要循环遍历一遍也是没谁了。
脑海里多了个概念,一个类似游标指针的概念,指定它去哪个位置就去哪个位置获取元素,不知道叫游标合适不合适,总之游标很忙。在我的实现里的currNode就是经常跑头跑尾。
把链表操作完转成数组会提高效率,也许?
发现自己对异常不熟悉到大脑一片空白