数据结构之链表与线性表

数据结构之链表与线性表

线性表(顺序线性表)

  1. 顺序表(顺序线性表):使用数组实现,一组地址连续的存储单元,数组大小有两种方式指定,一是静态分配,二是动态扩展。
    线性存储
  2. 优点:随机访问特性,查找O(1)时间,存储密度高;逻辑上相邻的元素,物理上也相邻;
    缺点:插入删除需移动大量元素。
    只需要输入对应的位置即可取出,所以复杂度为O(1),即查找一个元素的时间复杂度为O(Length),而平均查找长度
    ACN=(1+2+……+n)/n=(1+n)*n/(2n)=(n+1)/2。
  3. 特点:
    1.集合中必存在唯一的一个“第一元素”。
    2.集合中必存在唯一的一个 “最后元素” 。
    3.除最后一个元素之外,均有唯一的后继(后件)。
    4.除第一个元素之外,均有唯一的前驱(前件)。

链表

  1. 链表的定义是递归的,它或者为空null,或者指向另一个节点node的引用,这个节点含有下一个节点或链表的引用。
    与顺序存储相比,允许存储空间不连续,插入删除时不需要移动大量的元素,只需修改指针即可,但查找某个元素,只能从头遍历整个链表。
    抽象类型:
    private class Node{
    // 链表节点的嵌套类
    T item; // 节点内容
    Node next; // 后继节点
    }
  2. 单链表:使用任意存储单元来存储线性表中的数据元素,节点类型如上。
    单链表分为带头结点和不带头结点两种,不管有没有头结点,头指针都指向链表的第一个节点(有头结点指向头结点)。
    头结点:数值域可不设任何信息(头结点为空),头结点的指针域指向链表的第一个元素。
    使用头结点的好处:
    (1)链表第一位置节点上的操作和其它位置上的操作一致
    (2)无论链表是否为空,头指针都指向头结点(非空),空表和非空表处理一样。
    不使用头结点
    使用头结点
  3. 单链表代码:
    首先,构建节点类:
package DataStructures;
class ListNode{ ListNode( object theElement)
{ this( theElement, null);}
ListNode( object theElement, ListNode n){ 
element = theElement;
next = n;}object element;
ListNode next;} 

然后,构建驱动器:

package DataStructures;
//主要用于判断结点以及是否结束
public class LinkedListItr
{ LinkedListItr( ListNode theNode)
{ current = theNode;
}
public boolean isPastEnd( )
{ return current = = null;
}
public object retrieve( )
{ return isPastEnd( ) ? Null : current.element;
}
public void advance( )
{ if( ! isPastEnd( ) )
current = current.next;
}
ListNode current;
}

然后,是单链表:

package DataStructures;
public class LinkedList
{ public LinkedList( ){
 header = new ListNode( null ) ; }
 public boolean isEmpty( ){ 
 return header.next = = null ; }
 public void makeEmpty( ){ 
 header.next = null; }
 public LinkedListItr zeroth( ){ 
 return new LinkedListItr( header ) ; }
 public LinkedListItr first( ){ 
 return new LinkedListItr( header.next ) ; }
 public LinkedListItr find( object x )
 public void remove( object x )
 public LinkedListItr findPrevious( object x )
 public void insert( object x, LinkedListItr p )
 private ListNode header;
 //具体实现方法放在下面}

具体方法:

//打印链表
public static void printList( LinkedList theList )
{ if ( theList.isEmpty( ) )
System.out.print( “Empty list” ) ;
else
{ LinkedListItr itr = theList.first( );
for( ; ! Itr.isPastEnd( ); itr. Advance( ) )
System.out.print( itr.retrieve( ) + “ “ ) ;
}
System.out.println( );
}
//查找,O(N)
public LinkedListItr find (object x) 
{ ListNode itr = header.next;
while ( itr != null && !itr.element.equals( x ) )
itr = itr.next;
return new LinkedListItr( itr );
} 
//删除,O(1)
public void remove( object x )
{ LinkedListItr p = findprevious( x );
if( p.current.next != null )
p.current.next = p.current.next.next;
}
//查找上一个
public LinkedListItr findPrevious( object x ){ 
ListNode itr = header;
while( itr.next !=null && !itr.next.element.equals( x ))
	itr = itr.next;return new LinkedListItr( itr );}
//插入
public void insert( object x, LinkedListItr p){
 if( p!=null && p.current != null )
 	p.current.next = new ListNode( x, p.current.next );}

插入节点的几种算法:
待插入节点为s,一般采用后插法,即先找到插入位置节点的前驱节点,然后插入,时间复杂度O(n)

核心代码为:
p=getNodeByIndex(i-1);
s.next = p.next;
p.next = s;

还有一种方法是,直接插入到位置的后面(前插法),然后交换两个节点的值,插入的节点到了指定位置,时间复杂度O(1):

核心代码:
s.next = p.next;
p.next = s;
temp = p.item; // 交换内容
p.item = s.item;
s.item = temp;

4.双链表:单链表节点的缺点是只有一个后继节点,访问前驱节点只能从头遍历(如插入、删除),时间复杂度为O(n)。双链表,即添加一个指向前驱的节点,节点类型如下:
private class Node{
// 链表节点的嵌套类
T item; // 节点内容
Node prior, next; // 前驱节点和后继节点
}
双链表的查找和单链表的相同再次不在赘述,双链表的构造也分为头插和尾插,与单链表唯一不同的是修改前驱指针prior,具体见源码。插入和删除时不同,因为需要修改两个指针,如果给定要操作的节点,插入和删除的时间复杂度为O(1)。

注:插入删除操作同样也是先断后连。

  1. 插入节点

在p节点后插入s节点,先断后连,先把p和原后继节点的链条给断了,使后继节点只跟s节点有关:

①s.next = p.next; // 先断了p的后继
②p.next.prior = s; // 在断了p后继的前驱
③s.prior = p; // 让s的前驱指向p
④p.next = s; // p的后继指向s,重新连接上链条,此步必须在①②之后

  1. 删除节点

删除节点p的后继节点q,也是先断后连,把q和其后继节点的关系,转让给p即可:

①p.next = q.next; // 先断了q的后继
②q.next.prior = p; // 在断了q后继的前驱

删除节点q的前驱节点p,把p和去前驱节点的关系转让给q即可:
①q = p.prior.next; // 把p前驱节点的后继改成q
②q.prior = p.prior; // 把q的前驱节点改成p的前驱节点

5.循环链表

  1. 循环单链表

与单链表的区别在于,表中最后一个节点的指针不为null,而改为指向头结点(第一个节点),从而整个链表形成一个环。判断循环单链表是否为空,判断是否等于头指针。

只有一个尾指针的循环单例表,可以很方便的操作表头和表尾,因为尾指针的后继就是头指针O(1) 。

  1. 循环双链表(约瑟夫问题解法以及实现多项式的加法)

与双链表的区别在于,头结点的prior指针指向尾节点,尾节点的next指针指向头结点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值