链表问题算法难度不高,考察实现代码能力
线性结构,分为单链表和双链表。
1.不创建新节点的情况下进行链表排序,时间复杂度 nlogn.
解析:归并排序,每次找寻链表中间节点,并断开链表
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public class sortList {
public ListNode sortList(ListNode head) {
if(head == null || head.next ==null) return head;
ListNode mid = getMiddle(head);
//断开
ListNode midnext = mid.next;
mid.next=null;
//排序,合并
return merge(sortList(head),sortList(midnext));
}
/**
* 合并两个有序链表 递归实现
* @param head
* @return
*/
private ListNode merge(ListNode sortList, ListNode sortList2) {
// TODO Auto-generated method stub
if(sortList == null)
return sortList2;
if(sortList2 == null)
return sortList;
ListNode head = null;
if(sortList.val>=sortList2.val) {
head = sortList2;
head.next = merge(sortList,sortList2.next);
}
if(sortList.val<sortList2.val) {
head = sortList;
head.next = merge(sortList.next,sortList2);
}
return head;
}
/**
* 合并两个有序链表 非递归实现
* @param head
* @return
*/
private ListNode merge2(ListNode sortList,ListNode sortList2) {
//处理有一链表为空的情况
if(sortList==null)
return sortList2;
if(sortList2==null) {
return sortList;
}
//确定链表的头节点,并单独用一个变量记录
ListNode newhead = null;
ListNode res = null;
if(sortList.val<=sortList2.val) {
res = newhead = sortList;
sortList=sortList.next;
}else{
res = newhead = sortList2;
sortList2 = sortList2.next;
}
while(sortList!=null || sortList2!=null) {
if(sortList==null){
newhead.next = sortList2;
return res;}
else if(sortList2==null){
newhead.next=sortList;
return res;}
else if(sortList.val<=sortList2.val) {
newhead.next = sortList;
sortList=sortList.next;
newhead = newhead.next;
}else{
newhead.next = sortList2;
sortList2 = sortList2.next;
newhead = newhead.next;
}
}
return res;
}
/**
* 获取链表的中间结点,偶数时取中间第一个
* @param head
* @return
*/
public ListNode getMiddle(ListNode head) {
if(head==null || head.next==null)
return head;
ListNode fast,slow;
fast = slow =head;
while(fast.next!=null && fast.next.next!=null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
2. 采用创建新链表情况下进行链表排序
public class sortByInsertion {
public ListNode insertionSortList(ListNode head) {
ListNode dumy = new ListNode(Integer.MIN_VALUE);
ListNode cur = head;
ListNode pre = dumy;
while(cur!=null) {
ListNode next = cur.next;
pre = dumy;
while(pre.next!=null && pre.next.val<cur.val) {
pre =pre.next;
}
cur.next = pre.next;
pre.next = cur;
cur = next;
}
return dumy.next;
}
}
3. 深拷贝一个带有随机指针的双指针链表
思路:
1.在每个结点后面连接当前结点的拷贝结点
2. 让新结点直接联系起来
3. 在新结点中的随机指针联系起来
class RandomListNode {
int label;
RandomListNode next, random;
RandomListNode(int x) { this.label = x; }
}
public class copyListwithRandomPointer {
public RandomListNode copyRandomList(RandomListNode head) {
if(head==null || (head.next==null && head.random==null))
return head;
RandomListNode nod = head;
while(nod!=null) {
RandomListNode nod2 = copyNode(nod);
nod.next = nod2;
nod = nod.next.next;
}
nod = head.next;
while(nod!=null) {
//只有 非空值时候才能进行。next null.next 会运行时候报错。
//所以大都数情况下 nod.next!=null ,避免上述情况 ,但是本文有两个指针,所以分开讨论更合适
if(nod.next!=null)
nod.next = nod.next.next;
if(nod.random!=null)
nod.random = nod.random.next;
nod = nod.next;
}
return head.next;
}
private RandomListNode copyNode(RandomListNode nod) {
// TODO Auto-generated method stub
RandomListNode node = new RandomListNode(nod.label);
node.next = nod.next;
node.random = nod.random;
return node;
}
}
4. 链表是否有环判断
public class hasCycle {
public boolean hasCycle(ListNode head) {
if(head==null )
return false;
ListNode slow =head;
ListNode fast = head;
//下面的判断不能只判断后面的。
while(fast.next!=null && fast.next.next!=null) {
fast = fast.next.next;
slow = slow.next;
if(slow == fast)
return true;
}
return false;
}
}
5.
环形链表插入:有一个整数val,如何在一个有序的环形链表插入一个节点值为val 的节点,并且保证这个环形单链表依然有序。
思路:设置两个指针即可。
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class InsertValue {
public:
ListNode * insert(vector<int> A, vector<int> nxt,int val){
if(A.empty())
ListNode *head = new ListNode(val);
return head;
ListNode *head = new ListNode(A[0]),*p=head;
for(vector<int>::iterator iter=nxt.begin();iter!=nxt.end();iter++)
{
if(*iter==0)
{
(*p).next=Head;
break;
}
(*p).next=new ListNode(A[*iter]);
p=(*p).next;
}
ListNode *pre = head,*now =(*Head).next;
while(now!=head){
if((*pre).val<=val && (*now).val>=val){
ListNode *p = new ListNode(val);
(*p).next =now;
(Ipre).next = p;
break;
}
esle{
pre = now;
now = (*now).next;
}
}
if(now == head){
ListNode *p = new ListNode(val);
(*p).next = now;
(*pre).next p;
if(val<(*head).val)
head = p;
}
pre = head,p=(*head).next;
while(p!=head){
pre = p;
p = (*p).next;
}
(*pre).next = Null;
retrun head;
}
};
访问单个节点的删除:实现一个算法,删除单向链表中间的某个节点,你只能访问该节点。
思路:该节点复制后面节点,进一步删除后面节点,但如果是最后一个节点呢?
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class Remove {
public:
bool removeNode(ListNode* pNode)
{
if((*pnode).next==null)
return false;
ListNode *p = (*pNode).next,*pre = pNode;
While((*p).next!=null){
(*pre).val = (*p).val;
pre = p;
p = (*p).next;
}
(*pre).val = (*p).val;
(*pre).next = Null;
delete p;
return true;
}
};
链表分化 :对于一个链表,需要一个特定阀值对他进行分化,大于右边,小于左边。
思路额外空间:通过数组进行排序。
打印两个链表公共部分:现有两个升序链表,无重复元素,打印公共值部分。
class Common {
public:
vector<int> findCommonParts(ListNode* headA, ListNode* headB)
{
vector<int> res;
ListNode *p=headA,*q=headB;
while(p&&q)
{
if((*p).val==(*q).val)
{
res.push_back((*p).val);
p=(*p).next;
q=(*q).next;
}
else if((*p).val<(*q).val)
p=(*p).next;
else
q=(*q).next;
}
return res;
}
};
删除指定值:现在有一个单链表,给定一个值,将所有等于该值的val 节点删掉。尾插法
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class ClearValue {
public:
ListNode* clear(ListNode* head, int val)
{
if(!head)
return head;
ListNode *Head =null,*tail=null,*p = head,*temp = null
while(p){
temp = *p.next;
if((*p).val == val)
delete p;
else{
if(!=Head)
Head=p;
tail=head;
else
*tail.next = p;
tail=p;
*tail.next=null
}
p=temp;
}
return Head;
}
};
链表回文结构判断:给定一个链表,判断该链表是否是回文。
思路:元素进栈,然后出栈与原始链表对比
链表判环:判断一个单链表是否有环,有的话返回入环第一个节点。
思路:普通方法 通过hash 表实现,第一个重复出现的点
空间复杂度O(1) ,快慢两个指针实现
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class ChkLoop {
public:
int chkLoop(ListNode *head,int adjust){
if(!head)
return -1;
ListNode *slow=head,*fast = head;
while(((*fast).next&&(*(*fast).next).next)){
slow = (*slow).next;
fast =(*(*fast).next).next;
if(slow == fast)
break;
}
if(!(*fast).next||!(*(*fast).next).next)
return -1;
slow =head;
while(slow!=fast){
slow =(*slow).next;
fast=(*fast).next;
}
return (*fast).val;
}
};
无环单链表相交判断:现在有两个无环单链表,长度为n,M,请设计出一个时间复杂度O(n+M)的算法,判断两个是否相交。
思路:常用方法:使用一个哈希表添加第一个链表元素,并判断是否包含第二个链表中元素。
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class CheckIntersect {
public:
bool chkIntersect(ListNode* headA, ListNode* headB) {
if(!headA ||!headB)
return false;
int NumberA=0,NumberB=0,count=0;
ListNode *p = headA;
while(p){
NumberofA++;
p=(*p).next;
}
while(p)
{
NumofB++;
p=(*p).next;
}
ListNode *L=Null,*S=Null;
if(NumofA>NumofB)
L=headA,S=headB;
else
L=headB,S=headA;
count=(NumofA>NumofB)?(NumofA-NumofB):(NumofB-NumofA);
while(count--)
L=(*L).next;
while(L&&S)
{
if(L==S)
return true;
L=(*L).next;
S=(*S).next;
}
return false;
}
};