链表
- 数据存储在“节点中”
- 是真正的动态数据结构,不需要关注容量的问题
代码实现一个链表:
public class LinkedList<E> {
private class Node{
public E e;
public Node next;
public Node(E e , Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e,null);
}
public Node(){
this(null,null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node dummyhead;
private int size;
LinkedList(){
dummyhead = new Node(null,null);
size = 0;
}
//判断链表是否为空
public boolean isEmpty(){
return size == 0;
}
//返回链表存储元素个数
public int getSize(){
return size;
}
//利用头结点添加元素
/*public void add(E e,int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("传进来的索引不合法");
}
if (index == 0) {
addFirst(e);
} else {
Node prev = head;
for (int i = 1; i < index; i++) {
prev = prev.next;
}
Node node = new Node(e);
node.next = prev.next;
prev.next = node;
// prev.next = new Node(e,prev.next);
size++;
}
}*/
//利用虚拟头结点添加元素
public void add2(int index,E e){
if (index < 0 || index > size) {
throw new IllegalArgumentException("传进来的索引不合法");
}
Node prev = dummyhead;
for (int i = 1; i < index + 1; i++) {
prev = prev.next;
}
Node node = new Node(e);
node.next = prev.next;
prev.next = node;
// prev.next = new Node(e,prev.next);
size++;
}
//添加元素到链表头
public void addFirst(E e){
add2(0,e);
}
//添加元素到链表尾
public void addLast(E e){
add2(size,e);
}
public E get(int index){
if (index < 0 || index > size) {
throw new IllegalArgumentException("传进来的索引不合法");
}
Node curreet = dummyhead.next;
for (int i = 1; i < index + 1; i++) {
curreet = curreet.next;
}
return curreet.e;
}
public E getFirst(){
return get(0);
}
public E getLast(){
return get(size-1);
}
public void set(int index , E e){
if (index < 0 || index > size) {
throw new IllegalArgumentException("传进来的索引不合法");
}
Node curreet = dummyhead.next;
for (int i = 1; i < index + 1; i++) {
curreet = curreet.next;
}
curreet.e = e;
}
//根据索引删除指定节点
public E delete(int index){
if (index < 0 || index > size) {
throw new IllegalArgumentException("传进来的索引不合法");
}
Node prev = dummyhead;
for(int i = 1; i < index +1; i++){
prev = prev.next;
}
Node renode = prev.next;
prev.next = renode.next;
renode.next = null;
return renode.e;
}
public void deleteFirst(){
delete(0);
}
public void deleteLast(){
delete(size-1);
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
for(Node i = dummyhead.next;i != null;i = i.next){
sb.append(i.e+"->");
}
sb.append("null");
return sb.toString();
}
public static void main(String[] args){
LinkedList<Integer> l = new LinkedList<>();
for(int i = 0; i < 5; i++){
l.addFirst(i);
System.out.println(l);
}
l.add2(2,666);
System.out.println(l);
l.delete(2);
System.out.println(l);
}
}
说明:对于链表来说最重要的需要一个节点(node)的结构,节点里面存放元素以及一个next指针指向下一个节点(java里面虽然没有指针,但我认为指针可以更贴切的描述next),在这里使用内部类来实现这个节点的结构。对于链表来说还有一个重要的成员变量就是头结点,用来指向链表第一个节点,有了这个头结点就可以将链表遍历,但是上面的代码定义的是虚拟头结点,即虚拟出一个节点来指向头结点,因为在进行根据索引添加节点和删除节点的时候需要找到该节点前一个节点,但是头结点没有前一个节点,所以使用虚拟头结点可以更加方便的添加和删除而且不需要判断是否为头结点