题目
记一次高人指点的数据结构(双向链表)
实现以下功能
那么来试试吧~
首先是Node类
- 为了方便。。测试,加了个有参的构造方法
class Node<T> {
T value;
public Node(T value) {
this.value = value;
}
Node<T> next;
Node<T> pre;
}
接下来就是MyList的具体实现了
class MyList<T> {
Node<T> head;
Node<T> tail;
...
首先是:尾部新增元素
- 比较简单,只用判断一下是不是原来没有节点就行,因为要考虑到首尾指针指向首尾节点
void add(Node<T> element) {
if (head == null && tail == null) {
head = tail = element;
} else {
element.pre = tail;
tail.next = element;
tail = element;
}
}
然后是:在指定位置添加元素
- 因为是双向链表,所以不用遍历到index的前一个位置,直接遍历到index位置就好
- 然后判断一下是不是首尾节点,如果是的话操作少一半
- 如果不是的话也很简单。注意一下就是不要指针还没指向节点,节点就丢掉了。。
- 再注意一下index是否非法的问题。。如果index太大了,就默认在链表尾部添加节点
//指定位置新增元素
void add(Node<T> element, int index) {
if (index < 0) {
return;
}
Node<T> tmp = head;
// 先遍历到index的前一个
while (index != 0) {
if (tmp.next == null && index >= 1) {
//插在尾部分
tail.next = element;
element.pre = tail;
tail = element;
return;
}
tmp = tmp.next;
if (tmp == null) {
return;
}
index--;
}
// 如果是头结点
if (tmp == head) {
element.next = tmp;
tmp.pre = element;
head = element;
return;
}
if (tmp == tail) {
tmp.pre.next = element;
element.pre = tmp.pre;
element.next = tmp;
tmp.pre = element;
return;
}
element.next = tmp;
element.pre = tmp.pre;
tmp.pre.next = element;
tmp.pre = element;
}
其次是,获取指定位置元素
- 直接遍历到index位置,进行返回
- 如果idnex太大或者小于0,返回一个new(值为null)
//获取指定位置元素
Node<T> get(int index) {
if (index < 0) {
System.out.println("下标小于0");
return new Node<T>(null);
}
Node<T> tmp = head;
while (index != 0) {
tmp = tmp.next;
if (tmp == null) {
return new Node<T>(null);
}
index--;
}
return tmp;
}
删除指定位置的元素
- 这里也是判断一下是不是首尾节点,以及index下标。原理都是一样的。别搞丢节点就行
//删除指定位置元素
Node<T> remove(int index) {
if (index < 0) {
return new Node<T>(null);
}
Node<T> tmp = head;
Node<T> remove = head;
while (index != 0) {
tmp = tmp.next;
if (tmp == null) {
return new Node<T>(null);
}
index--;
}
remove = tmp;
if (tmp == tail && tmp == head) {
head = null;
tail = null;
return remove;
}
if (tmp == head) {
tmp = remove.next;
tmp.pre = null;
remove.next = null;
head = tmp;
return remove;
}
if (tmp == tail) {
tmp = remove.pre;
tmp.next = null;
remove.pre = null;
tail = tmp;
return remove;
}
tmp = remove.pre;
remove.next.pre = tmp;
tmp.next = remove.next;
remove.next = null;
remove.pre = null;
return remove;
}
反序比较复杂
- 这里使用了三个指针,tmp,tmp2,tmp3进行从尾向头遍历
- 一边遍历一边反序一个个节点
- 最后再交换一下原来首尾指针,达到反序的目的!
- 【还是要记得,别指针还没指向呢,就把节点搞丢了】
// 链表逆序,原来头部变尾部,第二个元素变倒数第二个元素。。。
void revert() {
if (head == tail) {
return;
}
Node<T> tmp = tail.pre;
Node<T> tmp2 = tail;
Node<T> tmp3 = tail;
while (tmp2 != head) {
tmp2.next = tmp;
tmp3 = tmp.pre;
tmp.pre = tmp2;
tmp.next = null;
tmp2.pre = null;
tmp2 = tmp;
tmp = tmp3;
}
tmp = tail;
tail = head;
head = tmp;
head.pre = null;
tail.next = null;
}
最简单的输出
- 几种情况判断一下,结束(0个,1个,多个)别忘记输出最后一个节点哦
//输出所有元素
void printElements() {
if (head == null && tail == null) {
System.out.println("MyList is Empty!");
} else {
if (head == tail) {
System.out.println(head.value);
return;
}
Node<T> tmp = head;
while (tmp != tail) {
System.out.println(tmp.value);
tmp = tmp.next;
}
System.out.println(tmp.value);//输出最后一个节点
}
}
}
这里是测试用例,供参考!
public class Test {
public static void main(String[] args) {
MyList<Integer> list = new MyList<>();
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node9 = new Node(9);
list.add(node4);
list.add(node5);
list.add(node9, 5);
list.revert();
list.printElements();
}
}
写在最后
反转双链表的操作其实还可以通过O(n)空间复杂度,也就是重新创建一条新链表进行实现,那么时间复杂度和空间复杂度就是双O(n),比较暴力。。最好还是使用指针。
有错还请指正!