顺序表
概念:
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
静态顺序表:使用定长数组存储。
动态顺序表:使用动态开辟的数组存储
接口实现:
1.打印顺序表
public void display() {
for(int i=0;i<this.usedSize;i++){
System.out.print(this.elem[i]+" ");
}
System.out.println();
}
2.在 pos 位置新增元素
public boolean isFull(){
return this.usedSize == this.elem.length;
}
public void add(int pos, int data) {
if(isFull()){
//Arrays.copyOf参数为被复制的数组和复制的长度
this.elem = Arrays.copyOf(this.elem,this.elem.length*2);
}
if(pos<0 || pos>this.usedSize){
System.out.println("该位置不合法");
return;
}
for(int i=this.usedSize-1;i>=pos;i--){
this.elem[i+1] = this.elem[i];
}
this.elem[pos] = data;
this.usedSize++;
}
3.判定是否包含某个元素
public boolean contains(int toFind) {
for(int i=0;i<this.usedSize;i++){
if(this.elem[i] == toFind){
return true;
}
}
return false;
}
4.查找某个元素对应的位置
public int search(int toFind) {
for(int i=0;i<this.usedSize;i++){
if(this.elem[i]==toFind){
return i;
}
}
return -1;
}
5.获取 pos 位置的元素
public int getPos(int pos) {
if(pos<0 || pos>=this.usedSize){
return -1;
}
return this.elem[pos];
}
6.给 pos 位置的元素设为 value
public void setPos(int pos, int value) {
if(pos<0 || pos>=this.usedSize){
return;
}
this.elem[pos] = value;
}
7.删除第一次出现的关键字key:首先要找到关键字key所在的位置,再进行删除
public void remove(int toRemove) {
int index = search(toRemove);
if(index == -1){
System.out.println("找不到");
return;
}
for(int i = index;i<this.usedSize-1;i++){
this.elem[i] = this.elem[i+1];
}
this.usedSize--;
}
8.获取顺序表长度
public int size() {
return this.usedSize;
}
9.清空顺序表
public void clear() {
this.usedSize =0;
}
缺点
1.顺序表中间或头部删除、插入数据时,时间复杂度为O(N);
2.扩容需要申请新空间,拷贝数据,释放原来的空间,会有很大的消耗;
3.增容一般是二倍增长,会有空间浪费。
链 表
概念
链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表的引用链接次序实现的。
实际中链表结构多样,以下情况组合起来就有8种链表结构:
单向、双向:
带头、不带头:
循环、非循环:
接口实现
1、无头单向非循环链表实现
无头单向非循环链表:
1.头插法:
public void addFirst(int data){
ListNode node = new ListNode(data);
if(this.head == null){
this.head = node;
}else{
node.next = this.head;
this.head = node;
}
}
2.尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
ListNode cur = this.head;
if(this.head == null){
this.head = node;
}else{
while(cur.next!=null){
cur = cur.next;
}
cur.next = node;
node.next = null;
}
}
3.任意位置插入,第一个数据节点为0号下标
public ListNode searchIndex(int index){
ListNode prev = this.head;
int count = 0;
while(count<index-1){
prev = prev.next;
count++;
}
return prev;
}
public boolean addIndex(int index,int data){
if(index<0 || index >=getLength()){
return false;
}
if(index == 0){
addFirst(data);
return true;
}
ListNode prev = searchIndex(index);
ListNode node = new ListNode(data);
node.next = prev.next ;
prev.next = node;
return true;
}
4.查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
ListNode cur = this.head;
while(cur !=null){
if(cur.data ==key){
return true;
}
cur = cur.next;
}
return false;
}
5.删除第一次出现关键字为key的节点
public void remove(int key){
//1.如果删除的是头节点
if(this.head.data == key){
this.head = this.head.next;
}
//2.找要删除的节点的前驱
ListNode prev = SearchPrev(key);
if(prev==null){
System.out.println("没有要删除的节点");
}
ListNode del = prev.next;
//进行删除
prev.next = del.next;
}
public ListNode SearchPrev(int key){
ListNode prev = this.head;
while(prev.next !=null){
if(prev.next.data==key){
return prev;
}
}
return null;
}
6.删除所有值为key的节点:先考虑中间或者尾巴的情况,再考虑头节点
public void removeAllKey(int key){
ListNode prev = this.head;
ListNode cur = this.head.next;
while(cur != null){
if(prev.next.data == key){
prev.next = cur.next;
cur = cur.next;
}else{
prev = cur;
cur = cur.next;
}
}
//最后处理头
if(this.head.data == key){
this.head = this.head.next;
}
}
7.得到单链表的长度:
public int getLength(){
ListNode cur = this.head;
int count = 0;
while(cur !=null){
count++;
cur = cur.next;
}
return count;
}
8.打印单链表数据
public void display(){
if(this.head==null){
return;
}
ListNode cur = this.head;
while(cur!=null){
System.out.print(cur.data+" ");
cur = cur.next;
}
System.out.println();
}
9.清空单链表
public void clear(){
ListNode cur = this.head.next;
if(this.head.next !=null){
this.head.next = cur.next;
}
this.head =null;
}
}
2、无头双向链表实现
无头双向链表:
(图中的200、400等为结点的引用,均为16进制,此处为了方便表示)
1.头插法
public void addFirst(int data){
ListNode node = new ListNode(data);
if(this.head == null){
this.head = node;
this.tail = node;
}else{
node.next = this.head;
this.head.prev = node;
this.head = node;
}
}
2.尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
if(this.head == null){
this.head = node;
this.tail = node;
}else{
this.tail.next = node;
node.prev = this.tail;
this.tail = node;
}
}
3.任意位置插入(第一个数据节点为0号下标):分三种情况 头插、尾插、中间插入。
中间插入要首先找到插入的位置,再进行插入操作。
public void addIndex(int index,int data){
if(index == 0){
addFirst(data);
return;
}
if(index == size()){
addLast(data);
return;
}
ListNode node = new ListNode(data);
ListNode cur = SearchIndex(index);
node.prev = cur;
cur.next = node;
cur.prev.next = node;
cur.prev = node;
}
public ListNode SearchIndex(int index){
if(index<0 || index>size()){
return null;
}
ListNode cur = this.head;
for(int i=0;i<index;i++){
cur = cur.next;
}
return cur;
}
4.查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
ListNode cur = this.head;
while(cur!=null){
if(cur.data == key){
return true;
}
cur = cur.next;
}
return false;
}
5.删除第一次出现关键字为key的节点:分三种情况:头节点、中间节点、尾结点
public void remove(int key){
ListNode cur = this.head;
while(cur != null){
if(cur.data == key){
//分三种情况
if(cur == this.head){
this.head = this.head.next;
this.head.prev = null;
}else{
//中间和最后位置
cur.prev.next = cur.next;
if(cur.next!=null){
cur.next.prev = cur.prev;
}else{
this.tail = cur.prev;
}
}
return;
}
cur = cur.next;
}
}
6.删除所有值为key的节点
public void removeAllKey(int key){
ListNode cur = this.head;
while(cur != null){
if(cur.data == key){
//分三种情况
if(cur == this.head){
this.head = this.head.next;
this.head.prev = null;
}else{
//中间和最后位置
cur.prev.next = cur.next;
if(cur.next!=null){
cur.next.prev = cur.prev;
}else{
this.tail = cur.prev;
}
}
}
cur = cur.next;
}
}
7.得到单链表的长度
public int size(){
ListNode cur = this.head;
int count = 0;
while(cur !=null){
count++;
cur = cur.next;
}
return count;
}
8.显示链表中的值
public void display(){
ListNode cur = this.head;
while(cur.next!=null){
System.out.println(cur.data+" ");
cur = cur.next;
}
System.out.println();
}
9.清空链表
public void clear(){
while(this.head != null){
ListNode cur = this.head.next;
this.head.next = null;
this.head.prev = null;
this.head = cur;
}
this.tail = null;
}
区别与联系
顺序表:
空间连续、支持随机访问
1.中间或前面部分的插入删除时间复杂度O(N)
2.增容的代价比较
链表:
以节点为单位存储,不支持随机访问
1.任意位置插入删除时间复杂度为O(1)
2.没有增容问题,插入一个开辟一个空