How I have handled this in the past (leaving out the part where we get a set of the record Ids were are processing stored in recordIds):
set<Id> existingApprovalsSet = new set<Id>();
list<ProcessInstance> existingApprovals = [SELECT Id, TargetObjectId
FROM ProcessInstance
WHERE TargetObjectId IN :recordIds];
if(existingApprovals.size() != 0){
for(ProcessInstance e : existingApprovals){
existingApprovalsSet.add(e.TargetObjectId);
}
}
for(SObject record : recordList){
if(!existingApprovalsSet.contains(record.Id)){
filteredRecordList.add(record);
}
}
This will give you a list of just the records that do not have an approval process. You can then continue processing them however you would like. What this doesn't give you is whether the record is locked or not, because that can be controlled on the Approval Process. You can adjust the SOQL query on ProcessInstance to account for the status of the Approval Process to try to account for this i.e.:
list<ProcessInstance> existingApprovals = [SELECT Id, TargetObjectId
FROM ProcessInstance
WHERE TargetObjectId IN :recordIds
AND Status = 'Pending'];
Again, this will depend on how the Approval Process is setup and whether it locks records on Submission, Approval, etc.