Leetcode链表类算法题目分析总结 C++ Python实现

Leetcode链表类算法题目分析总结与C++ Python实现

链表是常见的数据结构,一般由关键字和成员指针构成,为动态集合提供了一种简单而灵活的表示方法,其中单链表,由于结构简单而问题变化多样,在程序员技术笔、面试中最为常见,可以作为试金石快速考察被面试者的coding能力。笔者整理了leetcode和面试中常见的链表算法题,分类分析思路,力求举一反三与融会贯通,同时给出C++、Python的实现,方便大家夯实算法基础与准备算法刷题。

0. 链表的定义

  • 链表与数组比较,都是线性存储结构,区别是数组的线性顺序由下标决定,链表的顺序是由各个对象里的指针决定。
  • 双向链表的每个元素都是一个对象(C++ Primer中对对象的解释为具有类型的内存区域),每个对象有一个关键字和两个指针,还可以包含其他的数据。
  • 链表分为单链接(singly linked)的和双链接(double linked)的,已排序或未排序。
  • 链表元素的实现如下,

C++代码

//C++版链表结点定义
struct Node {
  Node(int data, Node* next=nullptr) {
    v=data;
    n=next;
 }
 int v;
 Node* n;
}

python代码

#python
#python3类定义可以省略显式继承object
class Node():
  def __init__(self, data, next=None):
    self.val=data
    #python无指针,变量均为引用形式,引用可以近似为指针,用引用代替指针
    self.next=next

1. 单链表的基本操作:建、查、插、改、删

1.1 链表的创建有头插法和尾插法两种

/* params
pHead:链表头指针的指针,创建的链表作为参数返回,若使用指针则必须使用引用形式;也可以将头指针做函数返回值
n: 链表长度
pdata: 若非空,则将数组元素顺序作为关键字
*/
void CreateLinklist(Node **pHead, int n, int *pdata = nullptr) {
  if (n < 1) {
    return;
  }
  if (pdata) {
    *pHead = new Node(pdata[0]);
  } else {
    *pHead = new Node(0);
  }
  Node *p = *pHead;
  for (int i = 1; i < n; i++) {
    Node *tmp;
    if (pdata) {
      tmp = new Node(pdata[i]);
    } else {
      tmp = new Node(i);
    }
    p->n = tmp;
    p = tmp;
  }
}

//头插法
Node *CreateLinklist(int data[], int n) {
  if (!data) {
    return nullptr;
  }
  Node *rear = new Node(data[n - 1]);
  Node *head;
  //头插法
  for (int i = n - 2; i >= 0; i--) {
    head = new Node(data[i]);
    head->n = rear;
    rear = head;
  }
  return head;
}

1.2 链表的查找

Node *ListSearch(Node* head, int key) {
  Node *p=head;
  while(p){
    if(p->v==key){
      return p;
    }
    p=p->n;
  }
  return p;
} 

1.3 链表的插入、删除

void InsertNode(Node **pHead, int Index, int Value) {
  if (Index < 0) {
    return;
  }
  Node *tmp = new Node(Value);
  if (Index == 0) {
    tmp->n = *pHead;
    *pHead = tmp;
    return;
  }
  int cnt = Index;
  Node *p = *pHead;
  while (cnt > 1 && p) {
    p = p->n;
    cnt--;
  }
  tmp->n = p->n;
  p->n = tmp;
}

//删除链表位置Index所在的节点
void DeleteNode(Node **pHead, int Index) {
  if (Index == 1) {
    Node *p = *pHead;
    *pHead = (*pHead)->n;
    delete p;
    return;
  }
  if (Index > 1) {
    int cnt = Index;
    Node *p = *pHead;
    while (cnt > 2 && p) {
      p = p->n;
      cnt--;
    }
    Node *cur = p->n;
    p->n = cur->n;
    delete cur;
  }
}
//删除整个链表
void DeleteLinkedList(Node **pHead) {
  if (*pHead == nullptr) {
    return;
  }
  Node *p = *pHead;
  Node *tmp = *pHead;
  while (p) {
    tmp = p;
    p = p->n;
    delete tmp;
  }
}

2. 常见单链表算法题

建议选择一个简单数据,按照代码逐行分析变量,画出链表图分析,熟能生巧。
2.1 题目:反转一个单链表
可以采用递归思路实现,将当前结点以后的链表完成反转,再把当前结点的下一个结点的next指向当前结点,当前结点的next指针置空。

Node *ReverseLinkedList(Node *head) {
  if (!head || !head->n) {
    return head;
  }
  Node *newhead = ReverseLinkedList(head->n);
  head->n->n = head;
  head->n = nullptr;
  return newhead;
}

考虑非递归方式如何实现,可以想象在平地上移动一艘船该如何做呢,取用多个圆木,依次垫底,从后往前放置,为了能够修改整个链表的顺序需要三个指针来完成这个目的。

//对参数传入的链表进行逆转
void ReverseLinkedList(Node **pHead) {
  if (*pHead == nullptr || (*pHead)->n == nullptr) {
    return;
  }
  Node *pre = nullptr;
  Node *cur = *pHead;
  Node *n = (*pHead)->n;
  while (cur) {
    n = cur->n;
    cur->n = pre;
    pre = cur;
    cur = n;
  }
  *pHead = pre;
}

用三个指针当作传递的圆木流动,也是解决链表的插入排序问题一个方法。
2.2 题目:链表上实现插入排序(Leetcode )
插入排序的基本思路是,每次从未排序的右侧元素去一个,插入到已经排好序的左侧的适当位置,相比对数组做插入排序,更加直观;需要考虑待插入元素是最小的,即要把这个结点放到头,所以需要构造一个关键字为无穷小的头指针dum,每次循环前pre置为头指针,cur为排好序的部分的尾结点,lat为待插入元素,循环中pre移动到待插入结点的左侧,并用tmp标出插入位置,插入结点,并保持链表完整。

void InsertSort(Node *&pHead) {
  if (!pHead || !pHead->n) {
    return;
  }
  Node *dum = new Node(INT_MIN);
  dum->n = pHead;
  Node *pre = dum;
  Node *cur = pHead;
  Node *lat = pHead->n;
  while (cur) {
    lat = cur->n;
    if (lat && cur->v > lat->v) {
      while (pre->n && pre->n->v < lat->v) {
        pre = pre->n;
      }
      Node *tmp = pre->n;
      pre->n = lat;
      cur->n = lat->n;
      lat->n = tmp;
    } else {
      cur = lat;
    }
    pre = dum;
  }
  pHead = dum->n;
}

2.3 两个链表做十进制加法

Node *AddTwoNumbers(Node *l1, Node *l2) {
  Node *dummy = new Node(0), *p = dummy;
  int carry = 0;
  while (l1 || l2 || carry) {
    if (l1) {
      carry += l1->v;
      l1 = l1->n;
    }
    if (l2) {
      carry += l2->v;
      l2 = l2->n;
    }
    p->n = new Node(carry % 10);
    carry /= 10;
    p = p->n;
  }
  return dummy->n;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值