数组和链表的区别:
集合框架就是将所有的数据结构,进行封装成了java自己的类,例如我们以后想实现一个顺序表,直接使用ArrayList就可以的;
主要是对数据的组织形式和描述方法是不一样的
1)顺序表的底层是一个数组,她是在逻辑上面和物理上面都是连续的
2)链表是一个有若干节点组成的一个数据结构,逻辑上面是连续的,但是在物理(内存)上面是不连续的;
3)顺序表适合查找相关的操作,因为可以通过使用下标直接获取到某个位置的元素,但是在链表中就没法通过下标来进行确定到底是哪一个位置的元素,所以链表不支持随机访问;
4)链表适合用于频繁的插入和删除操作,此时不需要像顺序表一样移动元素,顺序表插入元素,都要把元素放到后面去,顺序表删除元素,都要把元素移动到前面去;
链表的插入,只需要修改指向即可;
5)链表随用随取,要一个new一个节点,这个节点属于对象,头插,尾插时间复杂度是O(1)
但是顺序表,但是顺序表满了需要进行扩容,扩容后的空间也有可能是有些无法利用的,浪废掉了
下面我们来实现以下不带头结点的双向链表
在集合框架里面,有一个集合类,LinkedList就是用双向链表来进行表示的
我们想要实现增删查改功能
class ListNode{
int data;
ListNode next;//这是保存下一个节点的地址
ListNode front;//这是保存一个前驱结点的地址
public ListNode(int data)
{
this.data=data;
}
}
class DoubleLinkedList{
ListNode head=null;
ListNode tail=null;
public void display()
{
ListNode current=head;
while(current!=null)
{
System.out.println(current.data);
current=current.next;
}
}
public int size()
{ int count=0;
ListNode current=head;
while(current!=null)
{ count++;
current=current.next;
}
return count;
}
public boolean contains(int Key)
{
ListNode current=head;
while(current!=null)
{
if(current.data==Key)
{
return true;
}
current=current.next;
}
return false;
}
}
1.首先我们来进行实现头插法和尾插法
public void addFront(int data) {
ListNode node=new ListNode(data);
if(head==null)
{
head=node;
tail=node;
}else{
head.front=node;
node.next=head;
this.head=node;
}
}
public void addLast(int data)
{ ListNode node=new ListNode(data);
if(head==null)
{
head=node;
tail=node;
}else{
node.front=tail;
tail.next=node;
tail=node;
}
}
2.删除第一次出现的节点Key
当我们在单链表实现这个题的时候,我们必须要找到需要删除的关键字的前驱,假设Key=34,我们要找到关键字为Key的节点,还要找到前驱;那么在这里面我们只需要查询一个节点即可
public void removeKey(int Key)
{
ListNode current=head;
while(current!=null)
{
if(current.data==Key)
{
if(current==head)
{
head=head.next;
head.front=null;
return;
}else if(current==tail){
tail=tail.front;
tail.next=null;
return;
}else{
current.front.next=current.next;
current.next.front=current.front;
return;
}
}
current = current.next;
}
}
但是这个代码还是存在着一定的问题:
if(current==head)
{
head=head.next;
head.front=null;
return;}
1)如果我们在这里面要删除的是头结点,况且在整个代码中只有一个节点,这个双向链表中的唯一的头节点的front和next 域都是空的,如果我们此时执行这一段代码肯定就会发生空指针异常;
2)所以我们应该在这里面再次加上一个条件
3)如果我们想要删除所有Key那么就把所有的reurn去掉
if(current==head){
head=head.next;
if(head!=null){
head.front=null;
}else{
tail=null//走到这里说明只有一个节点
}
return;
}
3.像双向链表中插入元素(给定一个位置)
我们先定义一个结点current指向头节点,先走index位置
1)如果是插入的下标是第一个位置,那么就采用头插法
2)如果插入的位置是最后一个位置,就采用尾插法
public void addIndex(int index,int data)
{
if(index<0||index>size())
{
throw new UnsupportedOperationException("你所插入的下标位置不合法");
}else{
ListNode current=research(index);
if(current==head)
{
addFront(data);
}else if(current==tail)
{
addLast(data);
}else{
ListNode node=new ListNode(data);
node.next=current;
node.front=current.front;
current.front.next=node;
current.front=node;
}
}
}
4.清空双向链表中的所有节点
我们在这里面的核心思路是,既然要清空所有节点,那么我们终将要破坏链表的结构,所以我们可以让head一直向后走,head每走一步,我们都让head的next域和front域都变成空
public void clear() { ListNode curNext=null; while(head!=null) { curNext=head.next; head.front=null; head.next=null; head=curNext; } //注意我们此时要让所有的指向链表的节点都变成空 tail=null; }