顺序表和链表
1.线性表:是n个具有相同特性的数据元素的有限序列。
线性表是一种在实际中广泛使用的数据结构。
常见线性表:顺序表、链表、栈、队列、字符串…
线性表 逻辑上线性结构,物理结构上并不一定是连续的。
线性表在物理存储时,通常以数组和链式结构的形式存储。
2.顺序表:用一段物理地址连续的存储单元依次存储数据元素的线性结构。
依靠数组实现的一种数据结构。
一般采用数组存储,在数组上操作增删改查。
顺序表:物理上 逻辑上都连续
顺序表分为:👇
静态顺序表:使用定长数组存储 已知数据量。
动态顺序表:使用动态开辟的数组存储 灵活动态分配空间。
动态顺序表所需要的接口:
public class SeqList {
// 打印顺序表
public void display() { }
// 在 pos 位置新增元素
public void add(int pos, int data) { }
// 判定是否包含某个元素
public boolean contains(int toFind) { return true; }
// 查找某个元素对应的位置
public int search(int toFind) { return -1; }
// 获取 pos 位置的元素
public int getPos(int pos) { return -1; }
// 给 pos 位置的元素设为 value
public void setPos(int pos, int value) { }
//删除第一次出现的关键字key
public void remove(int toRemove) { }
// 获取顺序表长度
public int size() { return 0; }
// 清空顺序表
public void clear() { }
}
3.链表
链表:物理上不一定连续的,逻辑上一定是连续的
逻辑顺序通过引用链接
8种结构:单向、双向;带头、不带头;循环、非循环
带头:头节点作用标志头
1.单链表、双向链表:
2.不带头单链表、带头链表
3.单链表、循环单链表
重点:无头 单向非循环链表&无头 双向链表
无头 单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
无头 双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。
链表的实现
1、无头单向非循环链表实现 ---- 具体方法实现代码在文末
public class SingleLinkedList {
//1.头插法
public void addFirst(int data);
//2.尾插法
public void addLast(int data);
//3.任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data);
public int getLength() { //节点长度 及节点个数
private ListNode searchIndex(int index) { //找到index-1位置的节点
//4.查找是否包含关键字key是否在单链表当中
public boolean contains(int key);
//5.删除第一次出现关键字为key的节点
public void remove(int key);
private ListNode suchPrev(int key) { //找到删除节点的前驱节点
//6.删除所有值为key的节点
public void removeAllKey(int key);
//7.输出链表
public void display();
//8.清空链表
public void clear();
//9.链表长度 sout getLength返回值
public int size();
}
2、无头双向链表实现 ---- 具体方法实现代码在文末
public class DoubleLinkedList {
//头插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data);
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key);
//删除第一次出现关键字为key的节点
public void remove(int key);
//删除所有值为key的节点
public void removeAllKey(int key);
//得到单链表的长度
public int size();
public void display();
public void clear();
}
1、无头单向非循环链表实现👇
import org.omg.CORBA.DATA_CONVERSION;
class ListNode { //模板节点 - 类
public int data; //值,下个节点的地址
public ListNode next;
public ListNode(int data) { //构造函数 参数节点的值
this.data = data;
this.next = null;
}
}
class SingleLinkedList { //无头单向非循环链表
public ListNode head; //默认为 null
//1.头插法 不带头
public void addFirst(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node; //第一次插入
} else {
node.next = head; //node的下一个节点指向head地址
head = node; //将head地址变更为node地址
}
}
//2.尾插法
public void addLast(int data) {
ListNode cur = this.head;
ListNode node = new ListNode(data);
if (head == null) {
this.head = node; //第一次插入
} else {
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
//3.插入到index位置 位置号从0开始记 插入位置有getlength+1个
// getlength为节点个数 index=0;头插 index=getlength;尾插
public void addIndex(int index,int data){ //插入位置,插入值
if(index < 0 || index > getLength()) { //判断插入位置是否合法
System.out.print("位置不合法"+" ");
}
else if(index == 0) { //头插法
addFirst(data);
}
else{
ListNode prev = searchIndex(index); //找插入点的 前后
ListNode node = new ListNode(data); //new一个节点
node.next = prev.next; //新节点next指向 插入点后的next 与插入点后拼接
prev.next = node; //插入点的next指向新节点地址 与插入点前拼接
}
}
public int getLength() { //节点长度 及节点个数
int count = 0;
ListNode cur = this.head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
private ListNode searchIndex(int index) { //找到index-1位置的节点
//prev-->index-1; 返回当前节点的引用
ListNode prev = this.head;
int count = 0;
while (count < index-1) {
prev = prev.next;
count++;
}
return prev;
}
//4.查找是否包含关键字key
public boolean contains(int key){
ListNode cur=this.head;
while (cur!=null){
if(cur.data==key){
System.out.println("key值地址"+cur); //找到key值,输出了地址
return true;
}
cur=cur.next;
}
return false;
}
//5.删除第一次出现关键字为key的节点
public void remove(int key){
if(this.head == null) { //判断链表是否为空
return;
}
if(this.head.data==key){ //头节点值为key时
this.head=head.next; //删除头节点
}else{
if(searchPrev(key)==null){ //找不到删除节点 即前驱节点为空时
System.out.println("没有值为"+key+"的节点,无法删除。");
return;
}
ListNode prev=searchPrev(key); //创建节点,为搜索前驱节点的返回值
ListNode del=prev.next; //del节点为prev的next
prev.next=del.next; //删除。前驱指向=删除节点指向 拼接删除了del节点
}
}
private ListNode searchPrev(int key) { //找到删除节点的前驱 返回值为节点类型
ListNode prev = this.head;
while (prev.next!=null){ //删除节点的值不等于key时,删除节点向后移
if(prev.next.data==key){
return prev;
}
prev=prev.next;
}
return null;
}
//6.删除值为key的所有节点 返回头节点地址
public ListNode removeAllKey(int key){
if(head==null){ //若一个节点都没有
return null; //直接return 空
}
ListNode prev = this.head;
ListNode cur = this.head.next; //cur 在prev后一个节点
while (cur!=null){
if(cur.data==key){ //cur值为key
prev.next=cur.next; //prev指向 cur.next 后拼接
cur=cur.next; //cur向后移一个节点
}else{
prev=prev.next; //值不为key时
cur=cur.next; //prev cur 后移 继续循环
}
}
if(this.head.data==key){ //头节点等于key 删除 最后处理
this.head=this.head.next;
}
return head; //返回头节点地址
}
//7.输出链表
public void display() {
if (this.head == null) {
return;
} else {
ListNode cur = this.head;
while (cur != null) {
System.out.print(cur.data + " ");
cur = cur.next;
}
System.out.println();
}
System.out.println();
}
//8.清空单链表
public void clear(){
while(this.head.next!=null){
ListNode cur=this.head.next;
this.head.next=cur.next;
}
this.head=null;
}
//9.链表长度 sout getLength返回值
public void size(){
System.out.println("该链表长度:"+getLength());
}
}
2、无头双向链表实现👇
class ListNode{ //节点
public int data;
public ListNode next;
public ListNode prev;
public ListNode(int data){
this.data=data;
}
}
class DoubleList {
public ListNode head = null; //头
public ListNode last = null; //尾巴 每插入一次标志一次
//1.头插法
public void addFirst(int data) {
ListNode node = new ListNode(data);
if (this.head == null) {
this.head = node;
this.last = node;
} else {
this.head.prev = node;
node.next = this.head;
this.head = node; //头前移
}
}
//2.尾插法
public void addLast(int data) {
ListNode node = new ListNode(data);
if (this.last == null) {
this.head = node;
this.last = node;
} else {
this.last.next = node;
node.prev = this.last;
this.last = node; //尾巴后移
}
}
//3.任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
if (index==0){
addFirst(data);
return ;
}else if(index==size()){
addLast(data);
return ;
}
ListNode node=new ListNode(data);
ListNode cur=serchIndex(index); //cur为要插入的位置节点
if(cur == null) {
return ;
}
node.next = cur;
node.prev = cur.prev;
cur.prev.next = node; //先改prev.next,
cur.prev = node; //后改prev。 否则prev.next为node.next
}
//a.输出显示
public void display() {
ListNode cur=this.head;
while(cur!=null){
System.out.print(cur.data+" ");
cur=cur.next;
}
System.out.println( );
}
//b.找到插入位置
private ListNode serchIndex(int index){
if(index < 0 || index >= size()) {
return null;
}
ListNode cur=this.head;
while (index>0){
cur=cur.next;
index--;
}
return cur;
}
//c.得到单链表的长度
public int size(){
ListNode cur=this.head;
int length=0;
while (cur!=null){
length++;
cur=cur.next;
}
return length;
}
//-----------------------------------
//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) { //3种情况
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.last = cur.prev;
}
return ; //直接结束,删除第一个
}
}
cur=cur.next;
}
}
//6.删除所有值为key的节点
public void removeAllKey(int key){
ListNode cur=this.head;
while(cur!=null){
if(cur.data==key) { //3种情况
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.last = cur.prev;
}
//不return 全部删除
}
}
cur=cur.next;
}
}
//7.删除全部
public void clear(){
while(this.head!=null){
ListNode cur=this.head.next; //cur始终为head.next
this.head.next=null;
this.head.prev=null;
this.head=cur;
}
this.last=null; //last 也要处理
}
//查看清空方法:
// 1.cmd 2.jps(查看进程号) 3.jmap -histo:live 14272>e:log.txt 4.打开log.txt
}