Example 1:
Input: "()"
Output: true
Example 2:
Input: "()[]{}"
Output: true
Example 3:
Input: "(]"
Output: false
Example 4:
Input: "([)]"
Output: false
Example 5:
Input: "{[]}"
Output: true
class Solution {
public boolean isValid(String s) {
boolean flag=true;
if ((s.length() & 1) == 1)
return false;
Stack<Character> sta=new Stack<Character>();
for(char ch:s.toCharArray()){
if(ch=='('){
sta.push(')');
}
else if(ch=='['){
sta.push(']');
}
else if(ch=='{'){
sta.push('}');
}
else if (sta.isEmpty() || sta.pop() != ch){
return false;
}
}
return sta.isEmpty();
}
}
JAVA 堆栈类(Stack)的使用
堆栈是一种 “后进先出” (LIFO) 的数据结构, 只能在一端进行插入(称为 “压栈” ) 或删除 (称为“出栈”)数据的操作。
JAVA 中,使用 java.util.Stack 类的构造方法创建对象。
public class Stack extends vector
构造方法 : public Stack() 创建一个空 Stack。
方法: 1. public push (item ) 把项 压入栈顶。其作用与 addElement (item ) 相同。
参数 item 压入栈顶的项 。 返回: item 参数 ;
2. public pop () 移除栈顶对象,并作为函数的值 返回该对象。
返回:栈顶对象(Vector 对象的中的最后一项)。
抛出异常 : EmptyStackException 如果堆栈式空的 。。。
3. public peek() 查看栈顶对象而不移除它。。
返回:栈顶对象(Vector 对象的中的最后一项)。
抛出异常 : EmptyStackException 如果堆栈式空的 。。。
4. public boolean empty (测试堆栈是否为空。) 当且仅当堆栈中不含任何项时 返回 true,否则 返回 false.
5. public int search (object o) 返回对象在堆栈中位置, 以 1 为基数, 如果对象 o是栈中的一项,该方法返回距离 栈顶最近的出现位置到栈顶的距离; 栈中最上端项的距离
为 1 。 使用equals 方法比较 o 与 堆栈中的项。。。
参数: o 目标对象;
A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.
Example:
Input: "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
class Solution {
public List<String> letterCombinations(String digits) {
HashMap<Integer, String[]> hashmap=new HashMap<>();
hashmap.put(2,new String[]{"a","b","c"});
hashmap.put(3,new String[]{"d","e","f"});
hashmap.put(4,new String[]{"g","h","i"});
hashmap.put(5,new String[]{"j","k","l"});
hashmap.put(6,new String[]{"m","n","o"});
hashmap.put(7,new String[]{"p","q","r","s"});
hashmap.put(8,new String[]{"t","u","v"});
hashmap.put(9,new String[]{"w","x","y","z"});
int len=digits.length();
List<String> res=new ArrayList<>();
for(int i=0;i<len;i++) {
int flag=Integer.parseInt(digits.substring(i, i+1));
String[] tmp=hashmap.get(flag);
List<String> list_tmp=new ArrayList<>();
if(i!=0){
for(int j=0;j<res.size();j++){
for(int k=0;k<tmp.length;k++){
list_tmp.add(res.get(j)+tmp[k]);
}
}
res=list_tmp;
}else{
for(int k=0;k<tmp.length;k++){
res.add(tmp[k]);
}
}
}
return res;
}
}
链表的基础知识以及题目
单向链表的产生以及遍历;
public class Node{
int data;
Node next;
public Node(object data){
this.data=data;
}
}
public class LinkList{
Node head;
Node current;
//添加元素
public void add(Object data){
if(head==null){
head=new Node(data);
current=head;
}else{
current.next=new Node(data);
current=current.next;
}
}//从这个部分开始遍历
public void print(Node node){
current=node;
if(current==null) return ;
while(current!=null){
System.out.println(current);
current=current.next;
}
}
//测试部分
public static void main(String [] args){
LinkList linklist=new LinkList();
for(int i=0; i<10;i++){
linklist.add(i);
}
linklist.print(list.head);
}
}
2求单向链表中节点的个数(要给出节点的起点,要是长度的话必然是头结点)
public int getLength(Node head){
int length=0;
if(head==null) return length;
current=head;
while(current!=null){
length++;
current=current.next;
}
return length;
}
3查找倒数第K个节点
//允许遍历得到长度的情况下
public int getLastNode(int index){
if(head==null) return ;
current=head;
int length=0;
while(current!=null){
current=current.next;
length++;
}
current=head;
for(int i=0;i<length-index+1;i++){
current=current.next;
}
return current.data;
}
//要是不允许遍历得到链表的长度
public int getLastNode(Node head,int index){
//Node head 给出要给的头结点
if(head==null) return ;
Node current2=head;
for(int i=0;i<index-1;i++){
current1=current1.next;
while(curent==null) return ;
}
Node current1=head;
while(curent2!=null){
current1=current1.next;
xurrent2=current2.next;
}
return current1.data;
}
4查找出中间节点,不允许遍历链表
public Node getMiddleNode(Node head){
if(head==null) return null;
Node first=head;
Node second=head;
while(second!=null&&second.next!=null){
first=first.next;
second=second.next.next;
}
return first;
}
5合并两个有序单向链表,合并之后仍然有序
public Node mergeLinkList(Node node1,Node node){
Node current;
Node head;
if(node1==null&&node2==null) return null;
if(node1==null) return node2;
if(node2==null) return node1;
if(node1.data>node2.data){
head=node1;
node1=node1.next;
current=head;
current=current.next;
}else{
head=node2;
node2=node2.next;
current=head;
current=current.next;
}
while(node1!=null&&node2!=null){
if(node1.data>node2.data){
current=node1;
node1.next;
current=current.next;
}else{
current=node2;
node2.next;
current=current.next;
}
}
while(node1!=null){
current=node1;
//赋值的时候不用遍历
}
while(node2!=null){
current=node2;
}
return current;
}
6单链表的反转
public Node reverseNode(Node head){
if(head==null||head.next==null) return head;
Node newNode;
Node temp;
Node current;
current=head;
while(current!=null){
temp=current
current.next=newNode;
newNode=current;
current=temp.next;
}
return newNode;
}
7从尾到头打印单向链表
public void printReverse(Node head){
Node current;
Node head;
if(head==null) return head;
current=head;
Stack<Node> stack=new Stack <Node>();
while(current!=null){
stack.push(current);
current=current.next;
}
while(!stack.isEmpty){
System.out.println(stack.pop());
}
}
8判断单链表是否有环
public boolean isCycle(Node head){
if(head==null||head.next==null) return false;
Node first=head;
Node second=second;
boolean flag=false;
while(second!=null){
first=first.next;
second=second.next.next;
if(first==second){
flag=true;
}
}
return flag;
}
9取出有环链表的长度;
public int getCycleLength(Node head){
int length=0;
if(head==null||head.next=null) return length;
Node first=head;
Node second=head;
Node node;
while(second!=null){
first=first.next;
second=second.next.next;
if(first==second) node=first;
}
//node再次走到node才是环的长度,因为会一直走下去;
Node current=node;
while(current!=null){
length++;
if(current==node) return length;
node=node.next;
}
return length;
}
10判断两个单向链表首次相交的节点位置,Y型链表,这个我面试的时候真的遇到过!!!!!
public Node getCommonNode(Node first,Node second){
if(frist==null||second==null) return null;
Node longerNode;
Node smallerNode;
int longerlength=0;
if(getLength(first)>getLength(second)){
longerNode=first;
smallerNode=second;
longerLength=getLength(first)-getLength(second);
}else{
longerNode=second;
smallerNode=first;
longerLength=getLength(second)-getLength(first);
}
for(int i;i<longerLength;i++){
longerNode=longerNode.next;
}
while(longerNode!=null&&smallerNode!=null){
if(longerNode==smallerNode) return longerNode;
longerNode++;
smallerNode++;
}
return null;
}
public int getLength(Node head){
if(head==null) return 0;
Node current=head;
int length=0;
while(current!=null){
length++;
current=current.next;
}
return length;
}
在单链表中,我们只能从头结点开始顺序遍历,最后才能到达尾结点。最后到达的尾节点却要先被比较,这听起来是不是像“先进后出”?于是我们就能想到利用栈的特点来解决这个问题:分别把两个链表的结点放入两个栈中,这样两个链表的尾结点就位于两个栈的栈顶,接下来比较下一个栈顶,直到找到最后一个相同的结点。
首先遍历两个链表得到它们的长度。在第二次遍历的时候,在较长的链表上走 |len1-len2| 步,接着再同时在两个链表上遍历,找到的第一个相同的结点就是它们的第一个交点。
这种思路的时间复杂度也是O(len1+len2),但是我们不再需要辅助栈,因此提高了空间效率。
这种思路中,我们需要利用两个辅助栈,空间复杂度是O(len1+len2),时间复杂度是O(len1+len2)。和一开始的蛮力法相比,时间效率得到了提高,相当于是利用空间消耗换取时间效率。
leetcode18 4数之和
Given array nums = [1, 0, -1, 0, -2, 2], and target = 0. A solution set is: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
class Solution {//回溯??估计是了,先把循环做了
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length < 4) {
return res;
}
Arrays.sort(nums);
for (int i = 0; i < nums.length - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int sum3 = target - nums[i]; // 后3个数之和需等于sum3
for (int j = i + 1; j < nums.length - 2; j++) {//j=i+1一直写成1+1,找了和我写的差不多的博客粘过去就是对的,我也不知道为啥,就瞅啊瞅,嗯。。。没毛病
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int sum2 = sum3 - nums[j]; // 后2个数之和需等于sum3
int left = j + 1, right = nums.length - 1;
while (left < right) {
if (nums[left] + nums[right] == sum2) {
List<Integer> quad = new ArrayList<>();
quad.add(nums[i]);
quad.add(nums[j]);
quad.add(nums[left]);
quad.add(nums[right]);
res.add(quad);
while (left < right && nums[left++] == nums[left]) {}
while (left < right && nums[right--] == nums[right]) {}
} else if (nums[left] + nums[right] < sum2) {
while (left < right && nums[left++] == nums[left]) {}
} else {
while (left < right && nums[right--] == nums[right]) {}
}
}
}
}
return res;
}
}
为什么不是全部寻找呢????因为有点多余.每个都不同,必然是取一个少一个...取全部长度也是可以的..
leetcode22
For example, given n = 3, a solution set is:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
class Solution {//随机生成还是其他别的什么
public List<String> generateParenthesis(int n) {
List<String> res=Arraylist<String>();
String str=new String();
generator(res,str,n,n);
return res;
}
public void generator(List<String> list,String str,int left,int right){
if(left==0&&right==0){
list.add(str);
return ;
}
if(left>0){
generator(list,str+"(",left-1,right);
}
if(left<right){
generator(list,str+")",left,right-1);
}
}
}
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head=new ListNode(0);
ListNode current=head;//虚假的头结点,真正的头结点是current.next
if(l1==null&&l2==null) return null;
if(l1==null) return l2;
if(l2==null) return l1;
while(l1!=null||l2!=null){
if(l1==null){
current.next=l2;
break;
}
if(l2==null){
current.next=l1;
break;
}
if(l1.val>l2.val){
current.next=l2;
l2=l2.next;
}else{
current.next=l1;
l1=l1.next;
}
current=current.next;
}
return current;
}
}
解答部分来自于:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
ListNode *reverseBetween(ListNode *head, int m,int n){
ListNode dummy(-1);//定义一个假的节点,不是指针,是一个局部变量。
dummy.next = head;//成为新的头节点,这样真链表的头节点就是dummy.next
//这时候,我们的head节点也有prev指针了,head和其他节点在前驱指针这一方面,就没有区别了。
ListNode *prev = &dummy;
for(int i = 0;i<m-1;i++){//我们的prev应该指向的是,第m个节点的前驱节点
prev = prev->next;
}//跳出循环后我们的prev就是指向第m个节点前驱节点
ListNode *const head2 = prev;///把这个前驱节点 记做head2,因为后面要采用头插法的方式来build 链表,什么是头插法,什么是尾插法,这个一定要弄清楚了。
prev = head2->next;///prev现在指的是第m个节点,
ListNode *curr = prev->next;///curr指向prev的下一个节点
for(int i = m;i<n;i++){///对于每一个i (m<i<=n)的节点,利用头插法插入到head2所在的链表中。
[===] ... [===] [===] [===]
head2 prev curr
prev->next = curr->next;///这一步之后curr就独立了
curr->next = head2->next;///1
head2->next = curr;///2 这一步和上一步就是头插法了
curr = prev->next;curr指针后移一个位置
}
return dummy.next;
}
leetcode29
Example 1:
Input: dividend = 10, divisor = 3
Output: 3
Example 2:
Input: dividend = 7, divisor = -3
Output: -2
做除法,要求不使用乘法,除法和mod运算符。返回dividend除以除数divisior的商
class Solution {
public int divide(int dividend, int divisor) {
if(dividend==Integer.MIN_VALUE&&divisor==-1) return Integer.MAX_VALUE;
if(dividend==0) return 0;
if(divisor==1) return dividend;
long a=Math.abs((long)dividend);
long b=Math.abs((long)divisor);
int num=0;
long sum=0;
while(b<=a){
int count=1;
sum=b;
while(sum+sum<=a){
count+=count;
sum+=sum;
}
a=a-sum;
num=count+num;
}
if((dividend>0&&divisor<0)||(dividend<0&&divisor>0)){
num=-num;
}
if(num>Integer.MAX_VALUE) return Integer.MAX_VALUE;
return num;
}
}
Example:
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6 //我猜用递归
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {//为什么会超时
public ListNode mergeKLists(ListNode[] lists) {
if(lists==null||lists.length==0) return null;
int begin=0;
int end=lists.length-1;
while(begin<end){
int mid=(end+begin-1)/2;
for(int i=0;i<=mid;i++){
lists[i]=merge2list(lists[i],lists[end-i]);
}
end=(end+begin)/2;
}
return lists[0];
}
public ListNode merge2list(ListNode l1,ListNode l2){
if(l1==null&&l2==null) return null;
if(l1==null) return l2;
if(l2==null) return l1;
ListNode head=new ListNode(0);
ListNode current=head;
while(l1!=null&&l2!=null){
if(l1.val>l2.val){
current.next=l1;
l1=l1.next;
}else{
current.next=l2;
l2=l2.next;
}
current=current.next;
}
while(l1==null){
current.next=l2;
//l2=l2.next;
//current=current.next;
}
while(l2==null){
current.next=l1;
//l1=l1.next;
//current=current.next;
}
return head.next;
}
}
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
int begin = 0, end = lists.length - 1;
while (begin < end) {
int mid = (begin + end - 1) / 2;
for (int i = 0; i <= mid; i++) {
lists[i] = merge2list(lists[i], lists[end - i]);
}
end = (begin + end) / 2;
}
return lists[0];
}
public ListNode merge2list(ListNode l1, ListNode l2) {
if (l1 == null && l2 == null) return null;
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode dummy = new ListNode(-1), cur = dummy;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
cur.next = l1;
cur = l1;
l1 = l1.next;
} else {
cur.next = l2;
cur = l2;
l2 = l2.next;
}
}
if (l1 != null) cur.next = l1;
if (l2 != null) cur.next = l2;
return dummy.next;
}
}
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length==0)
return null;
return merge(0,lists.length-1,lists);
}
public ListNode merge(int i,int j,ListNode[] lists) {
if(j<i)return null;
if(i==j)return lists[i];
int mid=i+(j-i)/2;
ListNode l=merge(i,mid,lists);
ListNode r= merge(mid+1,j,lists);
ListNode dummy =new ListNode(0);
ListNode runner= dummy;
while(l!=null && r!=null) {
if(l.val>r.val) {
runner.next=r;
r=r.next;
runner=runner.next;
}
else {
runner.next=l;
l=l.next;
runner=runner.next;
}
}
if(l==null && r==null)
return dummy.next;
if(l==null)
runner.next=r;
else
runner.next=l;
return dummy.next;
}
}
归并排序:将其分成两本份,分别排序好后,合并好再排序,需要额外的空间
快速排序:将其分成两部份(不一定相等),分别排序,不需要额外的空间。。
public Sort{
begin=0;
end=length-1;
mid=begin+end-1/2;
sort(begin,mid);
sort(mid+1,end);
}
leetcode 26
class Solution {
public int removeDuplicates(int[] nums) {
int num=0;
for(int i=0;i<nums.length;i++){
if(nums[i]!=nums[num]){
num++;
nums[num]=nums[i];
}
}
num+=1;
return num;
}
}
leetcode 21 下一个全排列,题目都没看懂的我真是个智障
就是将这个数组排列成下一个较大的数,如果已经是最大的了,就排列成最小的数。
我是这么想的,从后往前遍历数组,如果一直是逐渐增大的,则已经是最大的了,如果出现了一个下降的数,那么遍历就到此为止,因为这已遍历的部分就可以排列成下一个较大的数了
当找到这个突然下降的点A后,由于它后面已经排列为“最大”,即从前往后一直变小,所以应该从后面比A大的数中找最小的点B,然后A和B交换位置。这个时候以B开头,后面也是最大的排列,由于需要找下一个较大的数,应该把B后面的排列改为最小的,只需要将后面的数组顺序逆转一下即可。如果一直没有找到下降的点,则全部逆转即可。
class Solution {
public void nextPermutation(int[] nums) {
if(nums==null&&nums.length==0) return ;
//如果是逆序那么直接把它顺过来就好,其他是颠倒两个元素的问题。
int i=0;
int len=nums.length;
for(i=len-1;i>0;i--){
if(nums[i-1]<nums[i]){
continue;
}else{
for(int j=len-1;j>=i;j--){
if(nums[i-1]>=nums[j]){
continue;
}else{
int a=nums[i-1];
nums[i-1]=nums[j];
nums[j]=a;
}
break;
}
break;
}
}
swap(nums,i,len-1);
}
public void swap(int[] nums,int left,int right){
while(left<right){
int temp=nums[right];
nums[right]=nums[left];
nums[left]=temp;
left++;
right--;
}
}
}
leetcode33 搜索旋转排序数组
Example 1:
Input: nums = [4,5,6,7,0,1,2]
, target = 0
Output: 4
Example 2:
Input: nums = [4,5,6,7,0,1,2]
, target = 3
Output: -1
二分法:
class Solution {
public int search(int[] nums, int target) {
int ret=-1;
if(nums.length==0) return ret;
int begin=0;
int end=nums.length-1;
int mid=0;
while(begin<=end){
mid=begin+(end-begin)/2;
if(nums[begin]<nums[mid]){
if(nums[begin]==target){
return begin;
}else if(nums[mid]==target){
return mid;
}else if(nums[begin]<target&&nums[mid]>target){
end=mid-1;
}else {
begin=mid+1;
}
}else{
if(nums[end]==target){
return end;
}else if(nums[mid]==target){
return mid;
}else if(nums[mid]<target&&nums[end]>target){
begin=mid+1;
}else {
end=mid-1;
}
}//else
}//while
return ret;
}
}
leetcode 34 find first and last index of the target
Example 1:
Input: nums = [5,7,7,8,8,10]
, target = 8
Output: [3,4]
Example 2:
Input: nums = [5,7,7,8,8,10]
, target = 6
Output: [-1,-1]
class Solution {
public int[] searchRange(int[] nums, int target) {
//必然是二分法,但是第一个和最后一个index
int left=0;
int right=nums.length;
int mid=(left+right)/2;
int p=-1;
while(left<right){
if(nums[mid]==target){
p= mid;
break;
}else if(nums[mid]<target){
if(left==mid) break;
left=mid;
mid=(right+left)/2;
}else if(nums[mid]>target){
if(mid==right) break;
right=mid;
mid=(right+left)/2;
}
}
if(p==-1){
return new int[]{-1, -1};
}else{
int a=p,b=p;
while(a>0&&nums[a-1]==target){ a--;}
while(b<nums.length&&nums[b+1]==target){ b++;}
return new int[]{a,b};
}
}
}