链表与数组的区别:
两者区别:
(1)数组静态分配内存,链表动态分配内存。
(2)数组在内存中是连续的,链表是不连续的。
(3)数组利用下标定位,查找的时间复杂度是O(1),链表通过遍历定位元素,查找的时间复杂度是O(N)。
(4)数组插入和删除需要移动其他元素,时间复杂度是O(N),链表的插入或删除不需要移动其他元素,时间复杂度是O(1)。
数组的优点:
(1)随机访问性比较强,可以通过下标进行快速定位。
(2)查找速度快
数组的缺点:
(1)数组需要开发者自己维护下标
(2)数组开辟时必须指定数组的长度,如果存放的元素过多,需要开发者自己扩容数组
(3)当在数组的某些位置增加和删除元素时,还要编写代码处理元素的移动
链表的优点:
(1)插入和删除的效率高,只需要改变指针的指向就可以进行插入和删除。
(2)内存利用率高,不会浪费内存,可以使用内存中细小的不连续的空间,只有在需要的时候才去创建空间。大小不固定,拓展很灵活。
链表的缺点:
查找的效率低,因为链表是从第一个节点向后遍历查找。
链表:
链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
单链表:
链表的节点 = 数据域 + 引用域 节点与节点之间是通过引用域相连
链表物理存储单元是非连续的、非顺序的存储结构
循环单链表:
尾节点的 next 域指向头结点,进行循环操作
单链表:
(1)链表中每个元素包含一个称为节点的结构
(2)节点 = 数据域 + 引用域
(3)链表是一种物理存储单元上非连续、非顺序的存储结构
(4)链表在插入元素可以达到O(1)的复杂度,相比顺序表更快
(5)查找或访问特定编号的节点则需要O(n)的时间
例:设计一个带头节点的单链表
当删除或者增加一个节点时,只需要改变next的指向(O(1))
查找一个节点或者访问特定编号的节点(O(n))
//带头节点的单链表
class SingleLinkedListTakedHead<E>{
protected Node<E> head;
class Node<E>{
protected E element;
protected Node<E> next;
public Node(E data){
this.element = data;
}
}
public SingleLinkedListTakedHead(){
head = new Node<>((E)new Object());
}
//头插法 head之后去添加元素
public void addHead(E data){
//创建新节点
Node<E> newNode = new Node<>(data);
//绑定新节点的next
newNode.next = head.next;
//head.next指向新节点
head.next = newNode;
}
//尾插法
public void addTail(E data){
//创建新节点
Node<E> newNode = new Node<>(data);
//遍历当前链表,找到最后一个节点
Node<E> tmp = head;
while(tmp.next != null){
tmp = tmp.next;
}
//绑定最后的一个节点的next
tmp.next = newNode;
}
public boolean delete(E data){
//head节点需要单独处理
if(head.element == data){
head = head.next;
return true;
}
//删除某一个正常节点
Node<E> tmp = head;
while(tmp.next != null){
if(tmp.next.element == data){
tmp.next = tmp.next.next;
return true;
}
tmp = tmp.next;
}
return false;
}
//返回指定位置的节点
public Node<E> findValue(int index){
if(index < 0) return null;
Node<E> tmp = head;
while(tmp.next != null && index-- > 0){
tmp = tmp.next;
}
if(index <= 0) {
return tmp;
}
return null;
}
//得到当前链表的长度
public int getLength(){
int size = 0;
Node<E> tmp = head;
while(tmp.next != null){
size++;
tmp = tmp.next;
}
return size;
}
//遍历链表
public String toString(){
StringBuilder sb = new StringBuilder();
Node<E> tmp = head;
while(tmp.next != null){
sb.append(tmp.next.element+" ");
tmp = tmp.next;
}
return sb.toString();
}
}
public class GY20191007 {
public static void main(String[] args) {
SingleLinkedListTakedHead<Integer> linkedListTakedHead = new SingleLinkedListTakedHead<>();
linkedListTakedHead.addTail(1);
linkedListTakedHead.addTail(2);
linkedListTakedHead.addTail(3);
linkedListTakedHead.addTail(4);
linkedListTakedHead.addTail(5);
System.out.println(linkedListTakedHead.delete(5));
System.out.println(linkedListTakedHead.delete(2));
System.out.println(linkedListTakedHead.delete(10));
System.out.println(linkedListTakedHead.findValue(5));
System.out.println(linkedListTakedHead.findValue(4));
System.out.println(linkedListTakedHead.findValue(1));
System.out.println(linkedListTakedHead.findValue(3));
System.out.println(linkedListTakedHead.getLength());
System.out.println(linkedListTakedHead.toString());
}
}
不带头结点的单链表:
class SingleLinkedList<E>{
protected Node<E> head; //永远指向第一个有效节点
//节点类
class Node<E>{
protected E element; //数据域
protected Node<E> next;//引用域,用来连接链表中的节点
public Node(E data){
this.element = data;
}
}
}
逆序输出链表,用两种方法,栈或者递归
用栈的代码段:
//逆序输出链表,用栈
public void reverse(){
Stack<Node> stack = new Stack<>();
Node node = head.next;
while(node != null){
stack.push(node);
node = node.next;
}
while (stack.size() > 0) {
node = stack.pop();
System.out.print(node.element + " ");
}
}
用递归的代码段:
//逆序输出单链表,用递归
public static <E> void reversePrintList(SingleLinkedList<E>.Node<E> head){
//递归终止条件
if(head == null){
return;//处理办法
}
//提取重复逻辑,缩小问题规模
reversePrintList(head.next);
System.out.println(head.element + " ");
}
单链表的逆置代码段:
//逆置单链表
public static <E> SingleLinkedList<E>.Node<E> reverseList(SingleLinkedList<E>.Node<E> head){
SingleLinkedList<E>.Node<E> current = head; //当前节点
SingleLinkedList<E>.Node<E> prev = null;//当前节点的前一个
SingleLinkedList<E>.Node<E> newHead = null;//逆置后链表的头节点
while(current != null){
//当前节点的下一个
SingleLinkedList<E>.Node<E> next = current.next;
if(next == null){
newHead = current;
}
current.next = prev;
prev = current;
current = next;
}
完整代码:
class SingleLinkedList<E>{
protected Node<E> head; //永远指向第一个有效节点
//节点类
class Node<E>{
protected E element; //数据域
protected Node<E> next;//引用域,用来连接链表中的节点
public Node(E data){
this.element = data;
}
}
//添加
public void add(E data){
//创建一个新的节点
Node<E> newNode = new Node<>(data);
//分情况:空链表 head == null
if(head == null){
head = newNode;
}else{
//链表不为空,遍历至尾节点
Node<E> tmp = head;
while(tmp.next != null){
tmp = tmp.next;
}
//绑定新节点
tmp.next = newNode;
}
}
//添加一个新节点至指定位置
public boolean addPos(E data, int pos){
if(pos < 0 || pos > getLength()+1){
return false;
}
//创建新节点
Node<E> newNode = new Node<>(data);
//插入0号位置需要特殊处理
if(pos == 0){
newNode.next = head;
head = newNode;
return true;
}
//找到pos位置
Node<E> tmp = head;
for(int i=0; i<pos-1; i++){
tmp = tmp.next;
}
//tmp指向pos-1位置的节点
//绑定新节点
newNode.next = tmp.next;
tmp.next = newNode;
return true;
}
public boolean delete(E data){
//head节点需要单独处理
if(head.element == data){
head = head.next;
return true;
}
//删除某一个正常节点
Node<E> tmp = head;
while(tmp.next != null){
if(tmp.next.element == data){
tmp.next = tmp.next.next;
return true;
}
tmp = tmp.next;
}
return false;
}
public int getLength(){
int length = 0;
Node<E> tmp = head;
while(tmp != null){
length ++ ;
tmp = tmp.next;
}
return length;
}
public Node<E> getHead() {
return head;
}
public void setHead(Node<E> head) {
this.head = head;
}
public void show(){
Node<E> tmp = head;
while(tmp != null){
System.out.print(tmp.element + " ");
tmp = tmp.next;
}
System.out.println();
}
}
//循环单链表
class LoopSingleLinkedList<E>{
protected Node<E> head;
class Node<E>{
protected E element;
protected Node<E> next;
public Node(E data){
this.element = data;
}
}
public LoopSingleLinkedList(){
head = new Node((E)new Object());
head.next = head;
}
}
public class GY{
//逆序输出链表,用栈
public void reverse(){
Stack<Node> stack = new Stack<>();
Node node = head.next;
while(node != null){
stack.push(node);
node = node.next;
}
while (stack.size() > 0) {
node = stack.pop();
System.out.print(node.element + " ");
}
}
//逆序输出单链表,用递归
public static <E> void reversePrintList(SingleLinkedList<E>.Node<E> head){
//递归终止条件
if(head == null){
return;//处理办法
}
//提取重复逻辑,缩小问题规模
reversePrintList(head.next);
System.out.println(head.element + " ");
}
//逆置单链表
public static <E> SingleLinkedList<E>.Node<E> reverseList(SingleLinkedList<E>.Node<E> head){
SingleLinkedList<E>.Node<E> current = head; //当前节点
SingleLinkedList<E>.Node<E> prev = null;//当前节点的前一个
SingleLinkedList<E>.Node<E> newHead = null;//逆置后链表的头节点
while(current != null){
//当前节点的下一个
SingleLinkedList<E>.Node<E> next = current.next;
if(next == null){
newHead = current;
}
current.next = prev;
prev = current;
current = next;
}
return newHead;
}
public static void main(String[] args) {
SingleLinkedList<Integer> list = new SingleLinkedList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
// list.addPos(10, 2);
// list.addPos(20, 0);
// list.show();
SingleLinkedList<Integer>.Node<Integer> newHead = reverseList(list.getHead());
list.setHead(newHead);
System.out.println("逆置后的链表为:");
list.show();
}
}
合并两个有序的单链表,保证合并之后依然有序
public static <E extends Comparable<E>> SingleLinkedList<E>.Node<E> mergeLinkedList(
SingleLinkedList<E>.Node<E> head1, SingleLinkedList<E>.Node<E> head2){
//确定新链表的头节点
SingleLinkedList<E>.Node<E> curHead = null;
if(head1.element.compareTo(head2.element) < 0) {
curHead = head1;
head1 = head1.next;
}else{
curHead = head2;
head2 = head2.next;
}
SingleLinkedList<E>.Node<E> tmp = curHead;
while(head1 != null && head2 != null){
if(head1.element.compareTo(head2.element) < 0){
tmp.next = head1;
head1 = head1.next;
}else{
tmp.next = head2;
head2 = head2.next;
}
tmp = tmp.next;
}
if(head1 == null){
tmp.next = head2;
}
if(head2 == null){
tmp.next = head1;
}
return curHead;
}
public static void main(String[] args) {
//合并两个有序链表
SingleLinkedList<Integer> list1 = new SingleLinkedList<>();
SingleLinkedList<Integer> list2 = new SingleLinkedList<>();
list1.add(1);list1.add(3);list1.add(5);list1.add(7);list1.add(9);
list2.add(2);list2.add(4);list2.add(6);list2.add(8);
list1.show();
list2.show();
SingleLinkedList<Integer>.Node<Integer> curHead = mergeLinkedList(list1.getHead(), list2.getHead());
SingleLinkedList<Integer>.Node<Integer> tmp = curHead;
while(tmp != null){
System.out.print(tmp.element + " ");
tmp = tmp.next;
}
System.out.println();
}
查找单链表中倒数第K个元素
//查找单链表中倒数第K个元素
public static <E> SingleLinkedList<E>.Node<E> lastK(
SingleLinkedList<E>.Node<E> head, int k){
if(head == null || k<0) return null;
//控制时间复杂度为O(n),只需要遍历链表一次实现
SingleLinkedList<E>.Node<E> front = head;
SingleLinkedList<E>.Node<E> behind = head;
for(int i=0; i<k-1; i++){
if(front.next != null){
front = front.next;
}else{
return null;
}
}
while(front.next != null){
front = front.next;
behind = behind.next;
}
return behind;
}
public static void main(String[] args) {
//倒数第k个节点
SingleLinkedList<Integer> list = new SingleLinkedList<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);
SingleLinkedList<Integer>.Node<Integer> kNode = lastK(list.getHead(), 7);
if(kNode != null){
System.out.println(kNode.element);
}else{
System.out.println(kNode);
}
}
不允许遍历链表, 在 pos之前插入
//不允许遍历链表, 在 pos之前插入
public static <E> boolean insertPosBefore(
SingleLinkedList<E> list,SingleLinkedList<E>.Node<E> pos, E data){
if(list.getHead() == null || pos == null) return false;
//创建新节点
SingleLinkedList<E>.Node<E> newNode = list.createNode(pos.element);
//插入新节点至pos之后
newNode.next = pos.next;
pos.next = newNode;
//改变pos位置的element为data
pos.element = data;
return true;
}
两个链表相交,输出相交节点
//两个链表相交,输出相交节点
public static <E> SingleLinkedList<E>.Node<E> commonNode(
SingleLinkedList<E> list1, SingleLinkedList<E> list2){
if(list1.getHead() == null || list2.getHead() == null) return null;
//计算两个链表的差值
int length1 = list1.getLength();
int length2 = list2.getLength();
int lengthDif = Math.abs(length1-length2);
SingleLinkedList<E>.Node<E> longHead = list1.getHead();
SingleLinkedList<E>.Node<E> shortHead = list2.getHead();
if(length1 < length2){
longHead = list2.getHead();;
shortHead = list1.getHead();
}
//长链表先走
for(int i=0; i<lengthDif; i++){
longHead = longHead.next;
}
//两个指针同时走
while(longHead != shortHead){
longHead = longHead.next;
shortHead = shortHead.next;
}
return longHead;
}
public static void main(String[] args) {
//构造两个相交的链表
SingleLinkedList<Integer> list1 = new SingleLinkedList<>();
list1.add(1);list1.add(3);list1.add(5); list1.add(7); list1.add(9);
SingleLinkedList<Integer> list2 = new SingleLinkedList<>();
list2.add(0);list2.add(2);list2.add(4);list2.add(6);
list1.show();
list2.show();
}
SingleLinkedList<Integer>.Node<Integer> head1= list1.getHead();
while(head1.element != 7){
head1 = head1.next;
}
SingleLinkedList<Integer>.Node<Integer> head2 = list2.getHead();
while(head2.next != null){
head2 = head2.next;
}
head2.next = head1;
list1.show();
list2.show();
}
SingleLinkedList<Integer>.Node<Integer> common = commonNode(list1, list2);
if(common != null){
System.out.println(common.element);
}else{
System.out.println(common);
}
单链表是否有环,环的入口节点是哪个
public class ClassParacticeTest {
//单链表是否有环,环的入口节点是哪个
public static <E> SingleLinkedList<E>.Node<E> ringNode(
SingleLinkedList<E>.Node<E> head){
//判断是否存在环,如果存在,则已知环中任意节点
SingleLinkedList<E>.Node<E> meeetingNode = isRing(head);
//说明环不存在
if(meeetingNode == null){
return null;
}
//计算环中节点的个数 length
while (front.next != front ) {
length++;
front = front.next;
}
//类比求倒数第k个节点的算法
front = head;
SingleLinkedList1<E>.Node<E> behind = head;
for(int i=0; i<length; i++){
front = front.next;
}
while(front.next != behind){
front = front.next;
behind = behind.next;
}
return behind;
}
}
双向链表: