链表
0. 结构体及必要说明
1 typedef struct LNode { 2 int data; 3 struct LNode *next; 4 }*LinkList;
1. 设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点。
递归:head->next=return;
栈(head):将值不等于 x 的节点用栈收集起来,收集完成后重新连接即可。
1 void del_x(LinkList &L,int x) { 2 if(L==NULL) 3 return; 4 if(L.data==x) { 5 LNode *temp=L; 6 L=L->next; 7 free(temp); 8 del_x(L,x); 9 } else 10 del_x(L->next,x); 11 } 12 /*递归*/ 13 ListNode* removeElements(ListNode* head, int val) { 14 if (head == NULL) 15 return head; 16 head->next = removeElements(head->next, val); 17 return head->val == val ? head->next : head; 18 } 19 /*Java:栈*/ 20 public ListNode removeElements(ListNode head, int val) { 21 Stack<ListNode> stack = new Stack<ListNode>(); 22 while (head != null) { 23 if (head.val != val) { 24 stack.push(head); 25 } 26 head = head.next; 27 } 28 while (!stack.isEmpty()) { 29 stack.peek().next = head; 30 head = stack.pop(); 31 } 32 return head; 33 }
ps:书上的答案是这样写的,但是我不太理解的是如果这样的话,那值为x的结点L虽然删除了,可L的前驱结点没能指向其后继结点整个单链不就断了吗...
破案了!由于该单链表不带头结点,第六行L为调用该函数的外层L->next,故这里实现了L->next=L->next->next,可自行尝试删除一条单链表的第1、2、3...个结点。
2.在带头结点的单链表L中,删除所有值为x的结点,并释放其空间,假设值为x的结点不唯一,试编写算法以实现上述操作。
双指针遍历引用(pre,cur,temp):pre指向头结点,cur指向pre->next;
1 void del_x(LinkList &L,int x) { 2 LNode *cur=L->next,*pre=L,*temp; 3 while(cur!=NULL) { 4 //这里的if可以任意指定内容 5 if(cur->data==x) { 6 temp=cur; 7 cur=cur->next; 8 pre->next=cur; 9 free(temp); 10 } else { 11 pre=cur; 12 cur=cur->next; 13 } 14 } 15 }
3. L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
递归:没啥好说的;
1 void ReversePrint(LinkList L) { 2 if(L->next!=NULL) 3 ReversePrint(L->next); 4 if(L!=NULL)//防止空链表的存在 5 cout << L->data << endl; 6 } 7 /*带返回值*/ 8 vector<int> v; 9 vector<int> ReversePrint(LinkNode *head) { 10 if(L==NULL) 11 return v; 12 ReversePrint(head); 13 v.push_back(head->data); 14 return v; 15 }
4.试编写在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)。
四指针遍历引用(pre,p,minpre,minp):先遍历,然后minpre->next=minp->next;
1 LinkList DelMin(LinkList &L) { 2 LNode *pre=L,*p=L->next; 3 LNode *minpre=L,*minp=L->next; 4 while(p!=NULL) { 5 if(p->data<minp->data) { 6 minpre=pre; 7 minp=p; 8 } 9 pre=p; 10 p=p->next; 11 } 12 minpre->next=minp->next; 13 free(minp); 14 return L; 15 }
5.试编写算法将不带头结点的单链表就地逆置,所谓“就地”是指辅助空间复杂度为0(1)。
指针反转(pre,head,next):注意刚开始pre=NULL;
1 //指针反转 2 LinkList Reverse(LinkList &head) { 3 ListNode *pre=NULL; 4 ListNode *next; 5 while(head) { 6 next=head->next; 7 head->next=pre; 8 pre=head; 9 head=next; 10 } 11 return pre; 12 }
6.有一个带头结点的单链表L,设计一个算法使其元素递增有序。
类似快排(pre,p,r):1、把pre,p置成一个链表,r置成一个链表;2、p作为后者的cur,r=p->head;在前者找到要插入的位置的前驱结点pre,把p插到pre后面。
1 void Sort(LinkList &L) { 2 LNode *p=L->next,*pre,*r=p->next; 3 p->next=NULL; 4 p=r; 5 while(p!=NULL) { 6 r=p->next; 7 pre=L; 8 /*找到要插入位置的前驱结点pre*/ 9 while(pre->next!=NULL&&pre->next->data<p->data) 10 pre=pre->next; 11 /*把p插到pre后面*/ 12 p->next=pre->next; 13 pre->next=p; 14 /*p是另一条链表的cur*/ 15 p=r; 16 } 17 }
7.请判断一个链表是否为回文链表。
栈(cur):把链表元素放进栈里,用栈顶元素和head进行验证。
1 stack<int> s; 2 bool isPalindrome(ListNode* head) { 3 stack<int> s; 4 ListNode *cur = head; 5 while(cur) { 6 s.push(cur->val); 7 cur = cur->next; 8 } 9 while(head) { 10 if(head->val != s.top()) 11 return false; 12 s.pop(); 13 head = head->next; 14 } 15 return true; 16 }
8.链表相交
1 ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { 2 ListNode *tempA=headA,*tempB=headB; 3 while(tempA!=tempB) { 4 tempA=(tempA==NULL)? headB:tempA->next; 5 tempB=(tempB==NULL)? headA:tempB->next; 6 } 7 return tempA; 8 }
9.删除任意链表中的重复元素
类似选择排序(cur,pre,next):双指针遍历(pre,next),遇到与cur相等值的元素改变指向。
1 ListNode* deleteDuplicates(ListNode* head) { 2 ListNode *cur = head; 3 ListNode *pre = NULL; 4 ListNode *next = NULL; 5 while (cur != NULL) { 6 pre = cur; 7 next = cur->next; 8 while (next != NULL) { 9 if (cur->val == next->val) { 10 pre->next = next->next; 11 } else { 12 pre = next; 13 } 14 next = next->next; 15 } 16 cur = cur->next; 17 } 18 return head; 19 }
10.将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
1.如果两个链表中有一个为空,说明无须合并过程,返回另一个链表的头节点即可。
2.比较 head1 和 head2 的值,小的节点也是合并后链表的最小节点,这个节点无疑应该是 合并链表的头节点,记为 head。head 节点所在的链表为链表 1,另一个链表为链表 2。
3. 用cur1 和 cur2遍历,然后根据大小关系做出不同的调整(cur1 小于 cur2,不做调整,反之,让 pre 的 next 指针指向 cur2),同时用一个变量 pre 表示上次比较时值较小的节点。
4. cur1先走完,就让pre->next接cur2;cur2先走完,散会!
1 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { 2 if(l1 == NULL || l2 == NULL) 3 return l1==NULL? l2:l1; 4 ListNode *head = l1->val < l2->val? l1 : l2; 5 ListNode *cur1 = head == l1? l1 : l2; 6 ListNode *cur2 = head == l1? l2 : l1; 7 ListNode *pre = cur1; 8 ListNode *next = NULL; 9 while(cur1!=NULL && cur2!=NULL) { 10 if(cur1->val <= cur2->val) { 11 pre = cur1; 12 cur1 = cur1->next; 13 } else { 14 next = cur2->next; 15 pre->next = cur2; 16 cur2->next = cur1; 17 pre = cur2; 18 cur2 = next; 19 } 20 } 21 pre->next = cur1==NULL? cur2 : cur1; 22 return head; 23 }
11.给定一个带有头结点 head
的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
快慢指针(pre,cur):注意:如果有两个中间结点,pre->next是第二个中间结点,pre是第一个中间结点。
1 ListNode* middleNode(ListNode* head) { 2 /*当链表只有两个结点时*/ 3 if (head->next==NULL) 4 return head; 5 /*当链表只有两个结点时*/ 6 if (head->next->next == NULL) { 7 return head->next; 8 } 9 ListNode *pre = head; 10 ListNode *cur = head; 11 while (cur->next != NULL && cur->next->next != NULL) { 12 pre = pre->next; 13 cur = cur->next->next; 14 } 15 /*如果有两个中间结点,pre->next是第二个中间结点,pre是第一个中间结点*/ 16 return cur->next!=NULL? pre->next:pre; 17 }
12.给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
1 TreeNode* sortedListToBST(ListNode* head) { 2 if(!head) 3 return NULL; 4 else if(!head->next) 5 return new TreeNode(head->val); 6 ListNode *pre=head,*cur=pre->next,*next=cur->next; 7 while(next && next->next){ 8 pre=pre->next; 9 cur=pre->next; 10 next=next->next->next; 11 } 12 pre->next=NULL; 13 TreeNode *root = new TreeNode(cur->val); 14 root->left = sortedListToBST(head); 15 root->right = sortedListToBST(cur->next); 16 return root; 17 }